多态
instanceof 和类型转换
- instanceof引用类型比较,两种对象的类型是否一样,返回true /false。判断一个对象是什么类型
public class InstanceofTest {
public static void main(String[] args) {
Object object = new Student();
System.out.println(object instanceof Student); //true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof String); //false
System.out.println("===========================");
Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
}
}
- 类型转换
- 父类引用引用指向子类对象
- 把子类转换为父类,向上转型,会丢失自己原来的一些方法
- 父类转换为子类,向下转型,强制转型,才能调用子类方法
- 方便方法的调用(转型)
向上转型:
父类创建了一个子类对象(父类 名称1=new 子类)此时,调不了子类的特有属性或者方法,可以调用父子类共有的方法
向下转型:
子类对象强制转换(子类 名称2=(子类)名称1)强制转换,此时可以调用子类的特有属性或方法
public class Test {
public static void main(String[] args) {
Person person1 = new Student();
person1.eat();
person1.sleep();
System.out.println("=============================");
//TODO 向上转型,丢失子类特有的方法
//不能调用 子类所特有的方法、属性, 编译的时候, person1是 Person
person1.name = "leilei";
person1.eat();
//TODO 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类的类型,导致编译时,只能调用父类中声明的属性和方法。
/*person1.height = 180; //无法调用
person1.goSchool();//无法调用
*/
System.out.println("=============================");
//TODO 如何调用子类所特有的属性和方法?
//TODO 使用强制类型转换,也称为向下转型
Student stu = (Student) person1;
stu.height = 180;
//使用强转,可能出现问题 ClassCastException
/*Women women = (Women) person1;
women.goShopping();*/
if (person1 instanceof Women) {
Women women = (Women) person1;
women.goShopping();
System.out.println("**************Women*************");
}
if (person1 instanceof Student) {
Student stu1 = (Student) person1;
stu1.goSchool();
System.out.println("**************Student*************");
}
if (person1 instanceof Person) {
System.out.println("**************Person*************");
}
if (person1 instanceof Object) {
System.out.println("**************Object*************");
}
}
}
练习题
定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形,MyRectangle代表矩形。
定义一个测试类GeometricTest,编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术), 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
public class GeometricObject {
private String color;
private double weight;
public GeometricObject(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double findArea(){
return 0;
}
}
public class Circle extends GeometricObject{
public static final double PI = 3.14;
private double radius;
public Circle(String color, double weight, double radius) {
super(color, weight);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public double findArea() {
return PI * radius * radius;
}
}
public class MyRectangle extends GeometricObject{
private double width;
private double height;
public MyRectangle(String color, double weight, double width, double height) {
super(color, weight);
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public double findArea() {
return width * height;
}
}
public class GeometricTest {
public static void main(String[] args) {
GeometricTest geometricTest = new GeometricTest();
GeometricObject circle = new Circle("red",4,5);
GeometricObject myrectangle = new MyRectangle("yellow",6,8,9);
System.out.println(geometricTest.equalsArea(circle,myrectangle));
System.out.println(geometricTest.displayGeometricObject(circle));
System.out.println(geometricTest.displayGeometricObject(myrectangle));
}
public boolean equalsArea(GeometricObject g1,GeometricObject g2){
return g1.findArea() == g2.findArea();
}
public double displayGeometricObject(GeometricObject geometricObject){
return geometricObject.findArea();
}
}
public class Piza {
private String name;
private double price;
private int size;
public Piza(String name, double price, int size) {
this.name = name;
this.price = price;
this.size = size;
}
public void display() {
System.out.println("披萨:" + name);
System.out.println("价格:" + price + "元");
System.out.println("尺寸:" + size + "寸");
}
}
public class Bacon extends Piza{
public Bacon(String name, double price, int size) {
super(name, price, size);
}
}
public class Seafood extends Piza{
public Seafood(String name, double price, int size) {
super(name, price, size);
}
}
public class Factory {
public static void product() {
Scanner input = new Scanner(System.in);
System.out.println("输入你要点的披萨(海鲜披萨或培根披萨):");
String food1 = input.nextLine();
System.out.println("输入你需要的披萨尺寸(6,12,20):");
int size = input.nextInt();
if (food1.equals("海鲜披萨")) {
Bacon bacon = new Bacon(food1, size * 12, size);
bacon.display();
} else if (food1.equals("培根披萨")) {
Seafood seafood = new Seafood(food1, size * 12, size);
seafood.display();
} else {
System.out.println("输入不正确,请联系工作人员!");
}
}
}
public class Order {
public static void main(String[] args) {
Factory factory = new Factory();
factory.product();
}
}
面试题
多态是运行时行为,还是编译时行为?
class Animal {
protected void eat() {
System.out.println("Animal eat food");
}
}
class Cat extends Animal {
@Override
protected void eat() {
System.out.println("Cat eat food");
}
}
class Dog extends Animal {
@Override
protected void eat() {
System.out.println("Dog eat food");
}
}
class Sheep extends Animal {
@Override
protected void eat() {
System.out.println("Sheep eat food");
}
}
class Test {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat();
case 1:
return new Dog();
default:
return new Sheep();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
Animal animal = getInstance(key);
animal.eat();
}
}
在运行完之后,可以看到运行结果并不是调用了Animal中 eat()方法,而是调用了判断之后,返回对象类型中的
eat()方法。这时就体现了多态是运行时的行为!
因为程序只能在程序运行的时候才能决定调用哪个对象的方法
object类使用
- Object是所有Java类的根父类
- 如果在类中的声明没有使用extends关键字指明父类,默认父类为java.lang.Object
- Object
方法 | 类型 | 描述 |
---|---|---|
public Object() | 构造 | 构造 |
public boolean equals() | 普通 | 对象比较 |
public int hashcode() | 普通 | 获取hashcode |
public String toString() | 普通 | 对象打印的时候使用 |
面试题 ==运算符与equals方法
==运算符:
- 可以使用在基本数据类型变量和引用数据类型变量中
- 如果比较的是基本数据类型,比较的是两个变量的字面值是否相等
- 如果比较的是引用数据类型,比较的是两个对象的地址是否相同,即两个引用是否指向同一个对象
equals()方法的使用:
- 只适用于引用数据类型
- Object类中定义的 equals() 和 == 的作用是相同,比较的是两个对象的地址值是否相同,即两个引用是否指向同一个对象。
- 像String、Date、File、包装类等都重写了Object类中定义的 equals()方法。比较两个对象的“实体内容”是否相同。
显然, 当equals为true时,==不一定为true。
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class ObjectTest {
public static void main(String[] args) {
int i = 100;
int j = 100;
double d = 10.0;
System.out.println(i == j);
System.out.println(i == d);
char c = 10;
System.out.println(i == c);
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);
String str1 = new String("BAT");
String str2 = new String("BAT");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
Person aa = new Person("aa", 18);
Person bb = new Person("aa", 19);
System.out.println(aa == bb);
System.out.println(aa.equals(bb));
}
}
toString()
像String、Data、File、包装类等都重写了Object类中的toString方法。
代码块 执行顺序(重点!!)
- 代码块的作用:用来初始化类、对象的
- 代码块如果有修饰的 话,只能使用static
静态代码块:
- 内部可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 作用:初始化类的信息
- 如果一个类中,定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行,优先于非静态代码块的执行
- 静态代码块内只能调用静态的属性、方法,不能调用非静态的结构。
非静态代码块:
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象时,对对象的属性进行初始化
- 如果一个类中,定义了多个非静态代码块,则按照声明的先后顺序执行
- 非静态代码块可以调用静态的属性,方法 或者 非静态的属性、方法。
静态区代码加载类时以弃被初始化,最早执行且只执行一次
先静态代码块(里面有静态方法就执行)
public class Test {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Test() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Test test = new Test();
System.out.println("===========");
Test test1 = new Test();
}
}
由父类到子类,静态先行
public class Root {//父亲
static {
System.out.println("Root 的静态初始化块");
}
{
System.out.println("Root 的普通初始化块");
}
public Root() {
System.out.println("Root 的 无参构造器");
}
}
class Mid extends Root {//儿子
static {
System.out.println("Mid 的静态初始化块");
}
{
System.out.println("Mid 的普通初始化块");
}
public Mid() {
System.out.println("Mid 的 无参构造器");
}
public Mid(String msg) {
this();
System.out.println("Mid 的 有参构造器,参数为" + msg);
}
}
class Leaf extends Mid {//孙子
static {
System.out.println("Leaf 的静态初始化块");
}
{
System.out.println("Leaf 的普通初始化块");
}
public Leaf() {
super("herb");
System.out.println("Leaf 的 无参构造器");
}
}
class LeafTest {
public static void main(String[] args) {
new Leaf();
}
}
抽象类
为什么需要抽象类?
类继承的主要作用在于可以扩充已有类的功能,但是对于之前的继承操作会发现,子类可以由自己的选择任意来决定是否需要覆写某一个方法,这个时候父类无法对子类做出强制性约定,这种情况下往往不会采用类的继承,而是必须要继承抽象类。
抽象类的主要作用在于对子类中的覆写方法进行约定,在抽象类里面可以定义一些抽象方法实现前面说的约定。
抽象方法指的是使用 abstract 关键字定义的并且没有提供方法体的方法。而抽象方法所在的类必须为抽象类,抽象类必须使用 abstract 关键字 进行 定义
abstract class Message {
private String type;
public abstract String getInfo();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class AbstractTest {
public static void main(String[] args) {
Message message = new Message();
System.out.println(message.getInfo());
}
}
上面的代码会编译失败
为什么?
抽象类是不能直接new 的
当一个抽象类定义完成之后,如果想要去使用抽象类。必须按照以下的规则:
- 抽象类必须提供子类,子类使用 extends 继承一个抽象类
- 抽象类的子类一定要 重写 抽象类的全部抽象方法
- 抽象类的对象实例化可以利用 对象多态性通过子类向上转型的方式完成
abstract class Message {
private String type;
public abstract String getInfo();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
class DataBaseMessage extends Message {
@Override
public String getInfo() {
return "连接";
}
}
public class AbstractTest {
public static void main(String[] args) {
Message message = new DataBaseMessage();
System.out.println(message.getInfo());
}
}
相关说明
- 在定义抽象类的时候决不能使用 final 关键字来定义,因为抽象类必须有子类,而 final 定义的类 断子绝孙
- 抽象类是作为一个普通类的加强版,只是追加了抽象方法。
- 抽象类中可以没有抽象方法,但是即便没有抽象方法,也无法使用关键字new 直接实例化抽象类对象
- 抽象类中可以提供 static 方法,并且该方法不受到抽象类对象的 局限
abstract class Message {
private String type;
public abstract String getInfo();
public static Message getInstance() {
return new DataBaseMessage();
}
}
class DataBaseMessage extends Message {
public DataBaseMessage() {
System.out.println("1111");
}
@Override
public String getInfo() {
return "连接";
}
}
public class AbstractTest {
public static void main(String[] args) {
Message message = new DataBaseMessage();
System.out.println(message.getInfo());
}
}
总结
abstract 修饰类:抽象类
抽象类不能实例化
抽象类中一定有构造方法,便于子类实例化时调用
abstract 修饰方法:抽象方法
抽象方法,只有方法的声明,没有方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
abstract 注意点:
- abstract 不能修饰变量、代码块、构造器
- abstract 不能用来修饰私有方法,静态方法,final的方法、final的类
- 但是抽象类中可以有static方法
练习
编写一个 Employee 类,声明为抽象类,
包含如下三个属性:name,id,salary。
提供必要的构造器和抽象方法:work()。
对于 Manager 类来说,他既是员工,还具有奖金(bonus)的属性。
请使用继承的思想,设计 CommonEmployee 类和 Manager 类,
要求类中提供必要的方法进行属性访问。
abstract class Employee {
private String name;
private int id;
private double salary;
public Employee() {
}
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
public class Manager extends Employee{
private double bonus;
public Manager(double bonus) {
this.bonus = bonus;
}
public Manager(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("管理员工");
}
}
public class CommonEmployee extends Employee{
@Override
public void work() {
System.out.println("生产产品");
}
}
public class EmployeeTest {
public static void main(String[] args) {
Employee prestige = new Manager("prestige", 1, 2000, 1000);
prestige.work();
CommonEmployee commonEmployee = new CommonEmployee();
commonEmployee.work();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7n4LSDD-1673527096163)(C:\Users\bkpp\AppData\Roaming\Typora\typora-user-images\image-20230108230038080.png)]
抽象类中有无参构造函数,有参构造函数,抽象方法,一定要重写抽象类的全部抽象方法,而有参无参函数可以根据用户的需求选择重写。
抽象类不能直接实例化,需经过子类向上转型 (父类是抽象类) :
子类 对象1 = new 子类();--------->父类 对象1 = new 子类()
lary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("管理员工");
}
}
public class CommonEmployee extends Employee{
@Override
public void work() {
System.out.println("生产产品");
}
}
public class EmployeeTest {
public static void main(String[] args) {
Employee prestige = new Manager(“prestige”, 1, 2000, 1000);
prestige.work();
CommonEmployee commonEmployee = new CommonEmployee();
commonEmployee.work();
}
}

抽象类中有无参构造函数,有参构造函数,抽象方法,一定要重写抽象类的全部抽象方法,而有参无参函数可以根据用户的需求选择重写。
抽象类不能直接实例化,需经过子类向上转型 (父类是抽象类) :
子类 对象1 = new 子类();--------->父类 对象1 = new 子类()