一、多态
多态是指一个程序中相同名字表示不同含义的情况
多态有两种类型:a.编译时多态 (重载 overload; 如p.sayHello() p.sayHello("11")) b.运行时多态: (覆盖override 子类对父类进行覆盖 运行时系统根据调用该方法的实例的类型来决定选择哪个方法调用 所有的非final方法都会自动的进行动态绑定 虚方法调用可以实现运行时多态 )
Java中,普通的方法是虚方法 但是static、private,final方法不是虚方法
1.什么是多态?
同类型的对象,变现出不同的形态 ;(使用父类类型作为参数,可以接受所有子类的对象)
多态的表现形式:父亲类型 对象名称 = 子类对象 ;
多态的前提:有继承关系,有父类引用指向子类对象(FU f = new Zi),有方法重写;
package polymorphismdemo4;
public class Test {
public static void main(String[] args) {
//创建对象
Student s = new Student("张三",18);
Teacher t = new Teacher("王老师",55);
Administrator a = new Administrator("李管理",33);
register(s);
register(t);
register(a);
}
//在测试类中写一个方法,既能接受老师,又能接受学生、管理员
//参数只能写成这个三个类型的父类
public static void register(Person p){
p.show();
}
}
2.多态调用成员的特点
调用成员变量:编译看左边,运行看左边
调用成员方法:编译看左边,运行看右边
package polymorphismdemo5;
public class Test {
public static void main(String[] args) {
//创建对象(多态方式)
Animal a = new Cat();
//调用成员变量
System.out.println(a.name); //输出动物
//调用成员变量:编译看左边,javac编译代码的时候,会看左边的父类有没有这个变量,如果有,编译成功;反之,编译失败
//运行看左边:java运行代码时,实际上获取的是左边父类成员变量的值
//如果是 Cat a = new Cat() 这种方式创建对象 System.out.println(a.name); 输出的是小猫
//调用成员方法
a.show();//运行的结果是Cat_____
//编译看左边:编译看左边,javac编译代码的时候,会看左边的父类有没有这个方法,如果有,编译成功;反之,编译失败
//运行看右边::java运行代码时,实际上运行的是子类中的方法
}
}
class Animal {
String name = "动物";
public void show(){
System.out.println("Animal_____");
}
}
class Cat extends Animal {
String name = "小猫";
@Override
public void show() {
System.out.println("Cat_____");
}
}
3.多态的优势
在多态的形式下,右边对象可以实现解耦合,便于拓展和维护
定义一个方法时,使用父类型作为参数,可以接受所有的子对象,体现多态的拓展性与便利
4.多态的弊端
不能调用子类特有的功能 解决方法:变回 子类类型
eg: Animal a = new Dog() Dog d = (Dog) a; 转换的时候不能瞎转,如果转换成其他类的类型就会报错
//完整代码:
//先判断是不是Dog类型,如果不是先强转成Dog类型,如果不是,转换之后变量名为d
if (a instanceof Dog d){
d.lookhome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
system.out.println("没有这个类型");
}
5. 上溯造型
将一种类型(子类)对象的引用转换成另外一种类型(父类)的对象引用(java中不需要任何特殊的标注,便允许上溯造型的使用)
上溯造型使得Java中允许创建不同类型对象的数组,但是这些类型必须都是数组声明类型或者数组声明类型的子类 如果声明的是object类,则数组中可以为任意数据类型
public class Main {
public static void main(String[] args) {
// 创建一个 Scanner 对象用于从控制台读取输入
Scanner sc = new Scanner(System.in);
// 读取一个整数 n,表示要创建的形状的数量
int n = sc.nextInt();
// 创建一个 Shape 类型的数组,长度为 n
Shape[] shapes = new Shape[n];
// 循环 n 次,创建不同的形状对象并存入数组
for (int i = 0; i < n; i++) {
// 读取一个字符串,判断是创建矩形还是圆形
String str = sc.next();
if ("rect".equals(str)) {
// 如果是矩形,读取长度和宽度,创建矩形对象并存入数组
int length = sc.nextInt();
int width = sc.nextInt();
shapes[i] = new Rectangle(length, width);
} else {
// 如果是圆形,读取半径,创建圆形对象并存入数组
int radius = sc.nextInt();
shapes[i] = new Circle(radius);
}
}
// 输出所有形状的周长之和,调用 sumAllPerimeter 方法计算并返回
System.out.println(sumAllPerimeter(shapes));
// 输出所有形状的面积之和,调用 sumAllArea 方法计算并返回
System.out.println(sumAllArea(shapes));
// 输出所有形状的信息,使用 Arrays.deepToString 方法将数组转换为字符串输出
System.out.println(Arrays.deepToString(shapes));
// 遍历形状数组,输出每个形状的类型和父类型
for (Shape shape : shapes) {
// 输出形状的类型
System.out.print(shape.getClass() + ",");
// 输出形状的父类型
System.out.println(shape.getClass().getSuperclass());
}
}
// 编写 double sumAllArea 方法计算并返回传入的形状数组中所有对象的面积和
public static double sumAllArea(Shape[] shapes) {
double sum = 0;
// 增强 for 循环遍历形状数组
for (Shape shape : shapes) {
// 将每个形状的面积累加到 sum 变量中
sum += shape.getArea();
}
// 返回面积之和
return sum;
}
// 编写 double sumAllPerimeter 方法计算并返回传入的形状数组中所有对象的周长和
public static double sumAllPerimeter(Shape[] shapes) {
double sum = 0;
// 增强 for 循环遍历形状数组
for (Shape shape : shapes) {
// 将每个形状的周长累加到 sum 变量中
sum += shape.getPerimeter();
}
// 返回周长之和
return sum;
}
}
6. 向下造型——对象的强制类型转换
将父类类型的对象强制变量强制转换为子类类型 Java中允许上溯造型的存在,使得父类类型的变量可以指向子类对象,但是通过该变量只能访问父类定义的方法和变量,子类特有的部分被隐藏;只有将父类类型强制转换为子类类型,才能通过该变量访问子类成员
7.
package polymorphismdemo6;
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// public void keepPet(Dog dog,String s){
// System.out.println("年龄为"+age+"岁的"+name+"养了一只"+dog.getColor()+"颜色的"+dog.getAge()+"岁的狗");
// dog.eat(s);
// }
// public void keepPet(Cat cat,String s){
// System.out.println("年龄为"+age+"岁的"+name+"养了一只"+cat.getColor()+"颜色的"+cat.getAge()+"岁的狗");
// cat.eat(s);
// }//方法重载
//想要一个方法,能够接受所有的动物,包括猫,包括狗
//方法的形参可以写这些类的父类
public void keepPet (fuAniaml a ,String s){
if(a instanceof Dog d){
System.out.println("年龄为"+age+"岁的"+name+"养了一只"+a.getColor()+"颜色的"+a.getAge()+"岁的狗");
d.eat(s);
}else if(a instanceof Cat c){
System.out.println("年龄为"+age+"岁的"+name+"养了一只"+a.getColor()+"颜色的"+a.getAge()+"岁的狗");
c.eat(s);
}else{
System.out.println("没有这种动物");
}
}
}
package polymorphismdemo6;
public class Test {
public static void main(String[] args) {
// Person p1 =new Person("小明",23);
// Dog d = new Dog (3,"黄w");
// p1.keepPet(d,"骨头");
//
// Person p2 =new Person("小红",19);
// Cat c = new Cat(3,"红");
// p2.keepPet(c,"鱼");
Person p = new Person("小天",16);
Dog d = new Dog(2,"黑");
Cat c = new Cat(3,"红");
p.keepPet(d,"骨头");
p.keepPet(c,"🐟");
}
}
二、包
包就是文件夹,用来管理各种不同功能的Java类
包是相关类和接口的一个集合,有效的解决了命名冲突的问题
1.包名的规则
公司域名反写+包的作用,全部英文小写
2.导包
a.使用同一个包中的类时,不需要导包
b.使用java.lang包中的类时,不需要导包
c.其他情况都需要导包
将package 引入源程序,格式:
import 包名.*;
import 包名. 类名;
d.如果同时使用两个包中的同类名,需要用全类名(包名.类名)
包定义语句在每个源程序中只能有一条, 即一个类只能属于一个包。
包定义语句必须在程序的第一行(之前可有空格及注释)。
包名用“.” 分隔。
3.包名与包中类的存储位置
包分隔符相当于目录分隔符,包存储的路径由包根路径加上包名指明的路径组成。包的根路径由CLASSPATH环境变量指出。
三、final关键字
final可以修饰方法:表示该方法是最终方法,不能被重写;
final修饰类:表示该类是最终类,不被继承;
final修饰变量:叫做常量,只能赋值一次;
final修饰的变量是基本类型,那么变量存储的数据值不能发生改变;如果修饰的是引用类型,那么变量存储的地址值不能发生改变,对象内部的可以发生改变
1.常量命名规则
单个单词全部大写;多个单词,全部大写,单词之间用下划线隔开
如果类的final变量在声明时没有赋初值,则所属类的每个构造方法中必须对该变量赋值;
如果未被赋初值的变量是局部变量,则可以在所属方法体中的任何位置对其赋值,但是只能赋一次
四、权限修饰符
用来控制一个成员能够被访问的范围的,可以修饰变量、方法、构造方法、内部类
实际开发中:一般只用private和public , 成员变量私有 方法公开(如果方法在的代码是抽取其他方法中共性代码,这个方法一般也私有)
对于类的成员变量和方法可以有这四种访问级别;对于类(除了内部类)只有public和default两种
同一个类的不同对象之间可以访问对方的私有变量和方法,访问控制是在类的级别上,而不是对象的级别上
p86
五、代码块
1.局部方法块:写在方法里面的局部代码块 提前结束变量的生命周期
2.构造代码块:写在成员位置的代码块;多个构造方法中重复的代码写在构造代码块里面;在创建本类对象的时候会优先执行构造代码块,在执行构造方法——(淘汰)
当多个构造方法有重复的代码时可以:
3.静态代码块
a.格式:static{}
b.特点:需要通过static关键字修饰,睡着类的加载二加载,并且自动触发,只执行一次
c.使用场景:在类加载的时候,做一些数据初始化的时候使用
六、object类
1.Object定义了所有对象都需要的状态和行为。如对象之间的比较、将对象转换为字符串、等待某个条件变量、当某条件变量改变时通知相关对象以及返回对象的类。
2.clone()方法
可以将一个对象复制给另外一个对象
aCloneableObject.clone()创建一个与其类型相同的对象,并将该对象成员变量的值初始化为aCloneableObject中成员变量的值
注意事项:提供复制能力的类必须自己实现Cloneable接口;该方法是浅复制,而不是深复制(如果被复制的对象成员是一个应用型变量,那么复制对象中将不包括该变量指向的对象
3.equals()方法
比较当前对象的引用是否与参数指向同一个对象
== 对于引用型变量比较的是这两个变量所指对象的地址,故比较两个字符串是否相同应该使用equals()方法 str1.equal(str2)
4.toString()
返回对象的字符串表示,表达的内容因具体的对象而异 。