常用IDEA快捷键(有部分自行定义)
删除当前行:ctrl+D
复制当前行至下一行:ctrl+Y
补全代码:alt+/
添加注释和取消注释:ctrl+/
导入该行需要的类:alt+enter
快速格式化代码:ctrl+alt+L
快速运行程序:alt+R
生成构造方法:alt+insert
查看一个类的层级关系:ctrl+H
定位到具体的方法:ctrl+B
自动分配变量:alt+enter .var
修饰符
| 本类 | 同包 | 子类 | 不同包 | |
|---|---|---|---|---|
| public | ✓ | ✓ | ✓ | ✓ |
| protected | ✓ | ✓ | ✓ | ✗ |
| 默认 | ✓ | ✓ | ✗ | ✗ |
| private | ✓ | ✗ | ✗ | ✗ |
封装
小技巧:可以将构造器和setXxx结合,在构造器中调用setXxx方法
继承
-
子类继承了所有的属性和方法,但是私有属性不能在子类中直接访问,要通过父类提供的公共方法去访问
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过
-
如果希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表 )
-
super在使用时,需要放在构造器的第一行
-
super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
-
java所有类都是Object类的子类,Object类是所有类的基类
-
父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)
-
子类最多只能继承一个父类(指直接继承),即java中是单继承机制
-
不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
继承本质详解
视频地址:https://www.bilibili.com/video/BV1fh411y7R8?p=294
继承的本质就是建立查找关系
方法重写
-
子类的方法的参数、方法名称要和父类方法的参数、方法名称完全一样
-
子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类
-
子类方法不能缩小父类方法的访问权限(public > protested > 默认 > private)
重载(overload)和重写(override)比较
发生范围:本类;父子类
方法名:必须一样;必须一样
形参列表:类型、个数或者顺序至少有一个不同;相同
返回类型:无要求;子类重写的方法,返回的类型和父类返回的类型一致或者是其子类
修饰符:无要求;子类方法不能缩小父类方法的访问权限
多态
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。
多态的前提是两个对象(类)存在继承关系。
-
方法的多态:
重写和重载就体现多态
-
对象的多态(核心):
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 号 的左边,运行类型看 = 号 的右边
//Animal类是父类,Dog类和Cat类是子类 Animal animal = new Dog();//animal编译类型是Animal,运行类型是Dog animal = new Cat();//animal的运行类型变成了Cat,编译类型仍然是Animal //例如Animal中有一个cry方法,Dog类中进行了重写 animal.cry();//因为运行时,执行到该行时,运行类型是Dog,所以cry是Dog中的cry
打个比方,便于理解:披着羊皮的狼。
表面上看像一只羊,但实际上他是一只狼。羊就好比是编译类型,而狼是运行类型。编译器看到的是编译类型,真正干活的时候是运行类型。
多态的向上转型
本质:父类引用指向子类对象
语法:父类类型 引用名 = new 子类类型();
特点:
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类中特有成员
- 最终运行效果看子类的具体实现!!!
//Cat类中有个特有的抓老鼠方法catchMouse(),但是下面的写法不能调用 //因为在编译阶段,能调用哪些成员,是由编译类型来决定的 animal.catchMouse();//会报错
编译的时候是javac来执行的,到了运行阶段是java来执行的,java不关心你的编译类型。(本质上还是一开始的基础,先编译再运行,编译的时候按照编译类型Animal来看,等到运行的时候再按照运行类型Cat来看)
不能理解的,可以看视频地址:https://www.bilibili.com/video/BV1fh411y7R8?p=310
多态的向下转型
语法: 子类类型 引用名 = (子类类型)父类引用;
特点:
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
//cat此时的编译类型和运行类型是什么呢? Cat cat = (Cat) animal;//编译类型是Cat,运行类型也是Cat cat.catchMouse();//成功 //“要求父类的引用必须指向的是当前目标类型的对象”,怎么理解这句话呢? //例子 Animal animal = new Dog(); Dog dog = (Dog) animal;//正确 Cat cat = (Cat) animal;//错误 //因为animal本质上就是Dog,所以转成Dog不犯错,但是下面这句话就相当于是把一只狗转换成一只猫 //当然错误,编译时不报错,运行时会报错,会报 类型转换异常
多态注意事项和细节讨论
属性没有重写之说,属性的值看编译类型
A instanceof B 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型(A是否是B类型或者是其子类型)
//例如有两个类,Sub类继承base类,都有count属性
class base {
int count = 10;
}
class Sub extends base {
int count = 20;
}
//在main方法中调用属性,输出的是哪一个呢
base base = new Sub();
System.out.println(base.count);//输出的是10
//因为count是属性,属性看编译类型,注意和上面方法调用的比较
//定义AA类和BB类,BB类继承AA类
class AA {}
class BB extends AA {}
//下面的判断是真还是假
BB bb = new BB();
System.out.println(bb instanceof BB);//true
System.out.println(bb instanceof AA);//true
动态绑定机制
- 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
//父类
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 sum() {
// return getI() + 20;
// }
// public int sum1() {
// return i + 20;
// }
public int getI() {
return i;
}
}
//main方法中
//请问输出什么?
A a = new B();
System.out.println(a.sum());//30
System.out.println(a.sum1());//20
解析
- a.sum():首先因为运行类型是B,所以调用B中的sum()方法,但是B中的sum()方法注释掉了,所以向上找到A中的sum()方法,发现A中的sum()方法可用,于是调用它,然后发现里面又调用了getI()方法,根据动态绑定机制,这个getI()方法调用的是B中的getI()方法,所以最终返回30
- a.sum1():和a.sum()类似,但由于返回的是i + 10,i是属性,不是方法,所以没有动态绑定机制,于是调用的就是A中的i,所以最终返回20
多态应用
多态数组
//Person是父类,Student和Teacher是它的子类
//Person有属性姓名name和年龄age,Student有特有的属性成绩score,Teacher有特有属性工资sal
//创建多态数组,都是向上转型,父类引用指向子类对象
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("marry", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//如何调用子类特有的方法?
//常用方法:利用instanceof
for(int i = 0; i < persons.length; i++) {
//调用公有方法
if(persons[i] instanceof Student) {
//Student特有方法
}else if(persons[i] instanceof Teacher) {
//Teacher特有方法
}else{
//......
}
}
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
Object类详解 equals方法
==和equals的对比
==是一个比较运算符
- 既可以判断基本类型,又可以判断引用类型
- 如果判断基本类型,判断的是值是否相等
- 如果判断引用类型,判断的是地址值是否相等,即判断是不是同一个对象
equals是Object类中的方法,只能判断引用类型,但经常在子类中会重写equals方法,用来比较对象的值是否相等
强烈建议:使用ctrl+B查看源码,可以理解的更加透彻,查看源码对能力的提升有很大帮助
hashCode方法
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同的对象,则哈希值是不一样的(不绝对)
- 哈希值主要根据地址来的,但不能完全将哈希值等价于地址
- 在集合中hashCode如果有需要的话,也会重写
toString方法
- 默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息
- 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString方法
- 当直接输出一个对象时,toString方法会被默认调用
//Object的toString()方法的源码
//getClass().getName() 类的全类名(包名+类名)
//Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//当直接输出一个对象时,toString方法会被默认调用
//比如下面这句话就会默认调用monster.toString()
System.out.println(monster);
finalize方法
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制(不一定立刻生效)
注意:对于finalize方法了解即可,因为实际开发中,几乎不会使用。
断点调试
重要提示:在断点调试过程中,是运行状态,是以对象的运行类型来执行的
IntelliJ IDEA断点调试如何查看源码:https://blog.csdn.net/qq_43544021/article/details/120633420?spm=1001.2014.3001.5501
**强烈建议:**可以经常进行断点调试,除了检查错误,更重要的是可以对Java的源码,运行机制等等知识点有更深入的理解(尤其是继承,多态这里,看源码真的很重要),而且不只是系统的源码,也可以看看人家高手大佬的代码,提高自己。



