包
包的三大作用
- 区分相同名字的类
- 当类很多的时候,可以很好地管理类
- 控制访问范围
包的基本语法
package xx.xxx
包的本质:实际上就是创建不同的文件夹保存类文件
java中常用的包
- java.lang.*
- java.unit.*
- java.net.*
访问修饰符
java一共提供了四种访问修饰符号,用于控制成员变量(方法)的访问权限
- public:公开
- protected:对子类和同一个包中的类公开
- 默认:没有修饰符,向同一个包中的类公开
- private:只有类本身可以访问,子类和其他都不能访问
背下此图(重要)
注意事项
- 只有默认修饰符和public才能修饰类,protected和private只能修饰类中的属性,方法,不能修饰类本身。
封装
定义:把抽象出来的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作。
封装的好处:
- 隐藏实现的细节,调用者不用关心实现细节
- 对数据进行验证,保证安全合理
封装实现步骤:
- 将属性私有化private(不能直接修改)
- 提供一个公共的(public)set方法,用于对属性的判断和赋值
- 提供一个公共的(public)get方法,用于获取属性的值
public void setXxx(类型 参数名) {
// 验证...
属性 = 参数名
}
public XX getXxx() {
// 权限判断
return xx
}
继承
为什么需要继承
- 提升代码复用性。继承可以解决代码复用,让编程更加接近人类思维,当多个类存在相同的方法和属性的时候,可以抽象出父类,在父类中定义这些相同的方法和属性,所有的子类不需要重新定义这些属性和方法,只需要通过extends关键字继承。
- 代码的扩展性和维护性提高了
继承的细节
- 子类不能访问父类的私有属性和私有方法,应该通过父类提供的公共方法去访问。
- 子类必须调用父类的构造器,完成父类初始化。本质是在子类的构造器里面默认写了个
super()函数调用,这个函数就是执行父类无惨构造器的的意思。
class Child extends Person {
public Child() {
// 系统默认自带这句话并执行
// super()
}
}
- 当创建子类的时候,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则需要子类的构造器中使用super去指定使用父类中的哪个构造器,否则编译不通过。
- 如果希望调用指定的父类构造器,则需要显式调用一下
super(参数列表) - super在使用时候,必须放在构造器的第一行。
- super()和this()都只能放在第一行,因此这两个不能同时存在同一个构造器中。
- Object类是所有类的父类(基类)
- 父类构造器的调用不限于直接父类,将一直往上追溯到Object类。
- java是单继承机制,子类只能继承一个父类。
继承的本质(重要)
super关键字
super代表父类的引用,用于访问父类的属性,方法,构造器。super不能访问父类的私有属性。
class Child extends Person {
public Child() {
// 访问父类的构造器
super()
// 访问父类的属性
System.out.println(super.name);
}
}
super细节
- 好处:当子类的父类方法或者属性重名的时候,可以通过这种方法调用。
- super的访问不限于父类,如果爷爷类也有对应的成员,也可以使用super去访问,多个基类的访问遵循就近原则。
super和this的比较
方法的重写/覆盖overrite
注意细节:
- 子类的方法名和参数要和父类完全一致。
- 子类的返回类型和父类的返回姚磊要一样,或者子类的返回类型是父类的返回类型的子类。
- 子类方法不能缩小父类方法的访问权限。
重载和重写的异同
多态
方法或对象具有多种形态,是面向的对象的第三大特征,多态是建立在继承的基础上。
对象的多态(重要)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象的时候就确定了,不能改变
- 运行类型可以改变
- 编译类型看定义时等号 的左边,运行类型看等号的右边。
多态的注意事项
- 多态的前提是:两个对象(类)存在继承关系
- 多态的向上转型
- 多态的向下转型
本质:
- 父类的引用指向了子类的对象
- 语法: 父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边。
- 可以调用父类的所有成员(要遵循访问权限),但不能调用子类的特有成员,因为在编译阶段,能调用哪些成员是由编译决定的,最终运行效果看子类的具体实现。
- 属性没有重写的概念,属性的值看编译类型。
java动态绑定机制(重要)
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
- 调用对象属性的时候,没有动态绑定机制,哪里声明,哪里使用。
public class Main {
public static void main(String[] arr) {
B b = new B();
// 由于B类没有sum方法,所以调用父类的sum方法,父类的sum方法调用子类的getI方法得到i为20,
// 为什么是调用子类的getI方法,因为java规定,调用对象的某个方法的时候,该方法会和该对象的内存地址进行绑定,
// 也就是你new了B类,调用方法的时候,永远先使用B类中的方法。
System.out.println(b.sum()); // 20 + 10 = 30
// 调用对象属性的时候,没有动态绑定机制,哪里声明,哪里使用,也就是调用了父类的sum1方法,那么父类中的变量i就直接拿父类的,
// 但是如果是方法的话,则会编程还是拿子类的
System.out.println(b.sum1()); // 10 + 10 = 20
// 一句话总结:属性就行,方法看运行
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int getI() {
return i;
}
}
一句话总结:属性就近,方法看运行
多态的应用
- 多态数组:数组定义为父类类型,里面保存的实际元素为子类类型
- 多态参数:方法定义的类型为父类类型,实际传参为子类类型
Object类详解
每个类都是使用Object类作为超类,所有对象(包括数组)都实现这个类方法(和JavaScript是一样的)
equals方法
==和equals的区别(经典面试题)
==是一个比较运算符
- ==既可以判断基本数据类型,又可以判断引用类型
- ==如果判断的是基本类型,则判断值是否相等
- ==如果判断的是引用类型,则判断的是内存地址是否相等
equals方法
- equals是Object类中的方法,只能判断引用类型。
- 默认的判断是地址内存地址相等,子类中往往重写该方法,用于判断
hashCode方法
返回该对象的哈希码值。
- 两个引用,如果指向同一个对象,哈希值肯定一样。反之则不一样
- 哈希值主要是根据地址来生成的,但不是完全等于地址
toString方法
返回该对象的字符串表示,默认返回的是:全类名 + @ + 哈希值的十六进制。
Egg egg = new Egg();
System.out.println(egg.toString()); // Egg@2f4d3709
class Egg {
public int i = 10;
public int sum() {
return this.i + 10;
}
}
finalize方法
当垃圾回收器要回收一个对象的时候,就会先调用这个方法,系统自动调用。
作用:这个方法默认什么都不做,但如果我们重写这个对象,那么就可以加入自己的逻辑,然后等垃圾回收的时候这个函数自动被调用。
注意细节:垃圾回收器,不是一个对象变成垃圾后,对象立刻就会被回收,而是有时机的(GC算法)。
这个函数实际开发很少用,但面试喜欢问。