系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 java基础
文章目录
1、面向对象
面向对象的三个基本特征是:封装、继承和多态。
- 封装可以隐藏实现细节,使得代码模块化
- 继承可以扩展已存在的代码模块。
- 多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用
封装和继承的目的都是代码重用,而多态则是为了实现接口重用
封装
什么是封装
封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式封装的好处
- 良好的封装能够减少耦合
- 类内部的结构可以自由修改
- 可以对成员进行更精确的控制
- 隐藏信息,实现细节。
继承
什么是继承
所谓继承:是指可以让某个类型的对象获得另一个类型的对象的属性的方法
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。食草动物和食肉动物又是属于动物类。所以继承需要符合的关系是:is-a,父类更通用,子类更具体。虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性继承的特性
- 子类拥有父类非private的属性和方法
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法
- 提高了类之间的耦合性
- 在Java中只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类,但是Java中却允许多层继承,多层继承就是,例如类C继承类B,类B继承类A,所以按照关系就是类A是类B的父类,类B是类C的父类,这是Java继承区别于C++继承的一个特性
继承条件下构造方法调用规则
- 如果子类的构造方法中没有通过super显示调用父类的有参构造方法,也没有通过this显示调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下写不写super()语句效果都是一样
- 如果子类的构造方法中通过super显示调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法
- 如果子类的构造方法中通过this显示调用自身的其他构造方法,在相应构造方法中应用以上两条规则
- 特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
super和this关键字
this
- 表示当前对象
- 调用本类中的方法或属性
- 调用本类中的构造方法时,需要放在程序首行
super
- 子类调用上级父类的方法或属性
- 调用父类中的构造方法时,需要放在程序首行
方法的重写与重载
- 方法的重写
子类从父类中继承方法,有时子类需要修改父类中定义的方法的实现,这称做方法的重写。当一个子类继承一父类,而子类中的方法与父类中的方法的名称、参数个数和类型都完全一致时,就称子类中的这个方法重写了父类中的方法。 - 重写的规则
- 重写的方法不能比被重写的方法有更严格的访问权限
- 重写的方法不能比被重写的方法产生更多的异常
- 方法的重载
方法重载是指多个方法可以享有相同的名字,但是参数的数量或类型不能完全相同。调用方法时,编译器根据参数的个数和类型来决定当前所使用的方法。方法重载为程序的编写带来方便,是OOP多态性的具体表现。在Java系统的类库中,对许多重要的方法进行重载,为用户使用这些方法提供了方便。 - 重载的规则
- 被重载的方法必须改变参数列表(参数个数或类型不一样)
- 被重载的方法可以改变返回类型
- 被重载的方法可以改变访问修饰符
- 被重载的方法可以声明新的或更广的检查异常
- 方法能够在同一个类中或者在一个子类中被重载
- 无法以返回值类型作为重载函数的区分标准。
抽象类
抽象类定义的规则
- 抽象类和抽象方法都必须用abstract关键字来修饰
- 抽象类不能被实例化,也就是说不能用new关键字去创建对象
- 抽象方法只需声明,而不需实现
- 含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
注意
:
- 在抽象类定义的语法中,方法的定义可分为两种:一种是一般的方法;另一种是“抽象方法”,它是以abstract关键字为开头的方法,此方法只声明了返回值的数据类型、方法名称与所需的参数,但没有定义方法体。
- 抽象类也可以像普通类一样,有构造方法、一般方法、属性,更重要的是还可以有一些抽象方法,留给子类去实现,而且在抽象类中声明构造方法后,在子类中必须明确调用。
final关键字
在Java中声明类、属性和方法时,可使用关键字final来修饰
修饰类:表示该类不能被继承
修饰方法:表示该方法不能被子类重写,但是可以重载
修饰变量:表示该变量一旦被赋值就不可以更改
修饰成员变量
- 如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。
- 如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始值
修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由程序员显示初始化。因此使用final修饰局部变量时,即可以在定义时指定默认值(后面的代码不能对变量再赋值),也可以不指定默认值,而在后面的代码中对final变量赋初值(仅一次)
修饰基本数据类型和引用数据类型
- 如果是基本数据类型,则其数值在初始化后便不可修改
- 如果是引用数据类型,则在对其初始化后便不能再让其指向另一个对象,但是对象内的属性可以修改
为什么局部内部类或匿名内部类只能访问局部final变量
因为内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。这里就会导致:当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有没有人再引用它时,才会死亡)。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的"copy"。这样就好像延长了局部变量的生命周期
将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,所以就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量 和方法的局部变量的⼀致性。这实际上也是⼀种妥协。使得局部变量与内部类内建立的拷贝保持⼀致。
接口
-
接口的定义
接口(interface)是Java所提供的另一种重要技术,它的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有以下两点不同- 接口里的数据成员必须初始化,且数据成员均为常量
- 接口里的方法必须全部声明为abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是“抽象方法”。
-
接口的实现
在Java中接口是用于实现多继承的一种机制,也是Java设计中最重要的一个环节,每一个由接口实现的类必须在类内部复写接口中的抽象方法,且可自由地使用接口中的常量。 既然接口里只有抽象方法,它只要声明而不用定义处理方式,于是自然可以联想到接口也没有办法像一般类一样,再用它来创建对象。利用接口打造新的类的过程,称之为接口的实现(implementation)。 -
接口的扩展
接口是Java实现多继承的一种机制,一个类只能继承一个父类,但如果需要一个类继承多个抽象方法的话,就明显无法实现,所以就出现了接口的概念。一个类只可以继承一个父类,但却可以实现多个接口。接口与一般类一样,均可通过扩展的技术来派生出新的接口。原来的接口称为基本接口或父接口,派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可加入新的成员以满足实际的需要。
同样的,接口的扩展(或继承)也是通过关键字extends来实现的。有趣的是,一个接口可以继承多个接口,这点与类的继承有所不同。
接口和抽象类的区别
1、定义上的区别
- 抽象类可以存在普通成员函数,而接口只能存在public abstract 方法。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
- 抽象类只能继承⼀个,接口可以实现多个
2、设计目的上的区别
-
接口的设计目的,是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制。
抽象类是对类本质的抽象,表达的是 is a的关系,比如:BMW is a Car。抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。
-
而抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生于一个抽象类。在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的。而A减B的部分,留给各个子类自己实现。正是因为A-B在这里没有实现,所以抽象类不允许实例化出来(否则当调用到A-B时,无法执行)。
接口是对行为的抽象,表达的是like a的关系。比如:Bird like a Aircraft(像飞行器一样可以飞),但其本质上is a Bird。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁、是如何实现的,接口并不关心。
- 使用场景上的区别
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度
多态
- 什么是多态
指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。 多态就是同一个接口,使用不同的实例而执行不同操作
2.多态的实现条件- 继承的存在(继承是多态的基础,没有继承就没有多态)
- 子类重写父类的方法(多态下调用子类重写的方法)
- 父类引用变量指向子类对象(子类到父类的类型转换)
2、JRE、JDK、JVM的区别
-
什么是 JDK?
JDK 的英文全称是 Java Development Kit。JDK是用于制作程序和Java应用程序的软件开发环境。Java 开发人员可以在 Windows、macOS、Solaris 和 Linux 上使用,是一个跨平台编程语言。JDK 帮助他们编写和运行 Java 程序。可以在同一台计算机上安装多个 JDK 版本。 -
什么是 JRE?
JRE 的英文全称是 Java Runtime Environment。JRE 是一个旨在运行其他软件的软件(有点绕口)。它包含类库、加载器类和 JVM。简单来说,如果你想运行 Java 程序,你需要 JRE。如果您不是程序员,则无需安装 JDK,只需安装 JRE 即可运行 Java 程序。不过,所有 JDK 版本都与 Java Runtime Environment 捆绑在一起,因此无需在 PC 单独下载和安装 JRE。JRE 的完整形式是 Java 运行时环境。 -
什么是JVM?
JVM 的英文全称是Java Virtual Machine。JVM 是一个引擎,它提供运行时环境驱动 Java 代码或应用程序。它将 Java 字节码转换为机器语言。JVM 是 Java 运行环境 (JRE) 的一部分。它不能单独下载和安装。要安装 JVM,您需要安装 JRE。JVM的就是Java虚拟机。在许多其他编程语言中,编译器为特定系统生成机器代码。但是,Java 编译器则称为 JVM 虚拟机生成代码。
-
主要区别
JDK是一个软件开发工具包,而JRE是一个允许Java程序运行的软件包,JVM则是一个执行字节码的环境。JDK的全称是Java Development Kit,JRE的全称是Java Runtime Environment,而JVM的全称是Java Virtual Machine。
JDK 是平台相关的,JRE 也是平台相关的,但是 JVM 不是平台相关的。
JDK 包含开发、调试等工具。JRE 包含类库和其他支持文件,而软件开发工具不包含在 JVM 中。
JDK 附带安装程序,另一方面,JRE 仅包含执行源代码的环境,而 JVM 捆绑在软件 JDK 和 JRE 中。
3、==和equals方法之前的区别
- ==:对比的是栈中的值,基本数据类型是变量值、引用类型是堆中内存对象的地址
- equals:object中默认也是采⽤==⽐较,通常会重写
4、以“HashSet如何检查重复”为例子来说明为什么要有hashCode
当对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,查看该位置是否有值,如果没有,HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
5、String、StringBuffer和StringBuilder
- String是不可变的,如果尝试修改,会产生一个新的字符串对象。改变的只是引用而已。而StringBuffer和StringBuilder对象是可以进行改变的
- String和StringBuffer是线程安全的。其中String因为不可变性,所以是安全的。而StringBuffer对象中使用synchronized关键字,所以也是安全的
- 所以在使用的时候推荐使用StringBuffer避免出现线程不安全问题,当然如果能保证是单线程调用的话,使用StringBuilder是最好的,因为他没有添加synchronized,效率比较高
6、List和Set的区别
- List: 有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用lterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标的元素
- Set:无序,不可重复,最多允许有一个Null元素对象,取元素时只能用lterator接口取得所有元素,在逐一遍历各个元素
7、ArrayList和LinkedList的区别
- 首先从底层数据结构上来说:ArrayList基于数组实现的,而LinkedList是基于链表来实现的
- 由于底层数据结构不同,所以使用场景也不同。ArrayList更适合于随机查找,LinkedList更适合于删除和添加操作。当然这个并不是绝对的。
- 另外ArrayList和LinkedList都实现了List接口,但LinkedList还额外实现了Deque接口,所以LinkedList还可以当作队列还使用(双端队列)
8、hashCode和equals之间的关系
- 如果两个对象相等,则hashCode⼀定也是相同的
- 两个对象相等,对两个对象分别调⽤equals⽅法都返回true
- 两个对象有相同的hashCode值,它们也不⼀定是相等的
- 重写equals方法,则hashCode方法也需要进行重写,例如在HashMap中会优先进行hashCode判断
- hashCode是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论论如何都不会相等,即使这两个对象指向相同的数据
9、Jdk1.7到Jdk1.8 HashMap 发生了什么变化?
底层结构不同
- jdk7中采用数组+链表的结构
- jdk8中采用数组+链表/红黑树的结构
链表插入元素的方式不同
- jdk7采用头插法
- jdk8采用尾插法。因为1.8中插入key和value时需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使用尾插法
10、深拷贝和浅拷贝
深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。
- 浅拷贝是指:只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
- 深拷贝是指:既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的属性指向的不是同一个对象
11、java中的异常体系
- Java中的所有异常都来自顶级父类Throwable。
- Throwable下有两个子类Exception和Error。
- Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
- Exception不会导致程序停止,又分为RunTimeException运行时异常和CheckedException检查异常。
- RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。如:NullPointerException、ClassCastException、IllegalArgumentException、IndexOutOfBoundsException
- CheckedException常常发生在程序编译过程中,会导致程序编译不通过。需要使用try catch进行处理