系列文章目录
文章目录
- 系列文章目录
- 一、多态
- 1、什么是多态?
- 2、多态的表现形式
- 3、多态的前提
- 4、多态的好处
- 5、多态调用成员的特点
- 6、多态的优势和弊端
- 7、引用数据类型的类型转换
- 二、包
- 1、什么是包?
- 2、包名的规则
- 3、使用其他类的规则
- 三、final 最终的,不可变的
- 四、权限修饰符
- 1、权限修饰符分类
- 2、权限修饰符的使用规则
- 五、代码块
- 1、局部代码块:提前结束变量的生命周期
- 2、构造代码块:创建本类对象时优先于构造方法执行,可以把多个构造方法中重复的地方放在构造代码块中。
- 3、静态代码块
一、多态
1、什么是多态?
同类型的对象,表现出不同的形态。
2、多态的表现形式
3、多态的前提
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法重写
4、多态的好处
使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
多态示例,如下图所示:学生,老师,管理员三各类都是Person的子类
5、多态调用成员的特点
变量调用:编译看左边,运行也看左边。
方法调用:编译看左边,运行看右边。
代码示例如下所示,注意看注释:
public class Test {
public static void main(String[] args) {
//创建对象(多态方式)
//Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
//运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a.name);//动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法。
a.show();///输出“Dog --- show方法”
//理解:
//Animal a = new Dog();
//现在用a去调用变量和方法的呀?是的
//而a是Animal类型的,所以默认都会从Animal这个类中去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下来。父:name 子:name
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal{
String name = "狗";
@Override
public void show() {
System.out.println("Dog --- show方法");
}
}
class Cat extends Animal{
String name = "猫";
@Override
public void show() {
System.out.println("Cat --- show方法");
}
}
内存图示意如下:为什么是左边或者右边是固定的,此图只是帮助理解记忆。在一个类存在父类时,在方法区都是先加载父类的字节码文件,再加载子类的字节码文件(.class文件)。所谓看左边就是在定义时(Animal a = new Dog())看a的左边是哪个类,然后找该类中是否存在或者使用其成员变量或者方法,编译时就是看有没有其成员变量或者方法,而运行时就是使用其成员变量或者方法。
6、多态的优势和弊端
优势:方法中,使用父类型作为参数,可以接收所有子类对象
弊端:不能使用子类的特有功能,此时需要使用强制类型转换,代码如下所示,注意理解注释:
public class Test {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//解决方案:
//变回子类类型就可以了
//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
//Cat c = (Cat) a;
//c.catchMouse();
/*if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}*/
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
}
}
class Animal{
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void lookHome(){
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
7、引用数据类型的类型转换
对于引用数据类型,在父类和子类的继承层次关系中,沿着子类向父类转换是向上转型,是自动转换的;沿着父类向子类转型是向下转型,必须使用强制类型转换。
二、包
1、什么是包?
包就是文件夹。用来管理各种不同功能的Iava类,方便后期代码维护。
2、包名的规则
公司域名反写+包的作用需要全部英文小写,见名知意。com.itheima.domain就是放一些实体类。比如com.itheima.domain.Student这才是类真正的名。
上图可以用导包简化如下所示:
3、使用其他类的规则
- 使用同一个包中的类时,不需要导包。
- 使用包中的类时,不需要导包。
- java.lang 其他情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名
三、final 最终的,不可变的
1.final修饰方法:最终方法,不能被重写
2.final修饰类:最终类,不能被继承
3.final修饰变量:是常量,不能被修改。基本数据类型:变量的值不能修改引用数据类型:地址值不能修改,内部的属性值可以修改。
final使用示例如下:增加代码可阅读性。
四、权限修饰符
权限修饰符:是用来控制一个成员能够被访问的范围的。
可以修饰成员变量,方法,构造方法,内部类,
1、权限修饰符分类
如下图所示,打对号表示可以使用。
其中同一个包中的其他类和不同包下的无关类的意思是要创建一个对象,然后用它来调用权限修饰符修饰的变量或者方法。
2、权限修饰符的使用规则
实际开发中,一般只用private和public。成员变量私有,方法公开。特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。
五、代码块
1、局部代码块:提前结束变量的生命周期
2、构造代码块:创建本类对象时优先于构造方法执行,可以把多个构造方法中重复的地方放在构造代码块中。
缺点:不够灵活,我有时候不想运行构造代码块中的代码,但无法让构造代码块不执行。比如有三个构造函数,其中只有两个我想执行构造代码块中的内容,这种情况我们不能在设置构造代码块,因为那样三个构造函数一定都会使得构造代码块被执行,我们需要如下图所示,我们把重复的语句不再放在构造代码块中,而是放在一个构造函数中,当调用其他构造函数创建对象时,我们通过构造函数调用其他包含重复代码的构造函数即可。
3、静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发只执行一次,也就是你创建多余一个对象只会执行一次(也就是内存中只会有一份,避免了被多次调用时候,存多份相同数据在内存中,占用内存的漏洞)。如下图所示。
作用:
- 初始化静态变量:静态代码块可以用于初始化类中的静态变量。在类加载时,静态代码块执行,可以对静态变量进行赋初值操作。
- 执行一次性初始化操作:静态代码块在类加载时只执行一次,因此非常适合执行一次性的初始化操作,如读取配置文件、建立数据库连接等。
- 预加载资源:静态代码块可以在类加载时预加载一些资源,如常用的数据,以提高程序的性能。