一、多态
1.1 介绍
多态:多种形态
代码:方法的多态性 --> 同一个方法,出现不同的功能 -> aniaml.play()
(属性没有多态性)
多态的好处:
- 扩展性强,让代码更灵活
- 减少耦合度 —> 开发思想:高内聚(即尽可能自己完成),低耦合(即使用别人的东西)
多态出现的前提:
- 继承/实现
- 重写
- 向上转型(父类引用指向子类对象)
1.2 示例
父类:
public class Animal {
String name;
public void eat(){
System.out.println("动物吃东西 ");
}
}
子类:
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//这也算是类型转换;小向大默认进行
//向上转型 -- 父类引用(父类对象)指向子类对象
Animal animal = new Dog();
Animal animal1 = new Cat();
/**
* 编译看父类,运行看子类
*/
animal.eat();//狗吃骨头
animal1.eat();//猫吃鱼
//new Dog - 子类
feedAnimal(new Dog());
feedAnimal(new Cat());
}
/**
* 多态的应用场景1:把方法的参数设计成父类
* 调用方法时传入子类,让方法出现多态性
* 目的:扩展性强
* @param animal
*/dd
public static void feedAnimal(Animal animal){
animal.eat();
}
}
1.3 instanceof【了解】
向上转型 :子类对象赋值给父类引用
向下转型:父类引用(对象)赋值给子类引用
- 注意:向下转型是必须先有向上转型,才能成功向下转型
public static void main(String[] args) {
//向上转型
Animal animal = new Dog();
//调用父类与子类都有的方法 -- 父类编译,子类运行
animal.eat();
//向上转型后,无法调用子类特有的方法
//animal.watch();
System.out.println("------------");
//向下转型
Dog dog = (Dog) animal;
//调用子类特有的方法
dog.watch();//狗会看门
System.out.println("-------------------");
feedAnimal(new Dog());
feedAnimal(new Cat());
System.out.println("------------");
//instanceof 可以调用子类特有方法
show(new Dog());
show(new Cat());
//System.out.println(cover(65));
}
//多态设计方法 -- 输出狗类与猫类重写父类的共有方法
public static void feedAnimal(Animal animal) {
animal.eat();
}
// 多态设计方法(instanceof) -- 输出狗类与猫类的特有方法
public static void show(Animal animal) {
/**
* instanceof 判断对象是否属于某个类的实例
*/
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watch();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.sleep();
}
}
ASCII码值
设计一个方法,将数字转成ASCII中的对应的字母
//数字65 ==> A 97 ==> a
//65~90 A~Z; 97~122 a~z;
//0-65535之间的整数可以赋值给char,char是对应的ascii值
//char c2 = 65;//A
public static String cover(int a) {
String result = “”;//作用域
if (a>=65 && a<=90 || a>=97 && a<=122){
char c = (char) a;//将int型强转为char型
result = c +“”;//任何数据拼接字符串,都会变成字符串
}else {
System.out.println(“err”);
}
return result;
}
测试方法
System.out.println(cover(65));//A
二、多态总结
2.1 总结
代码写法:类中有另外一个类的属性,部门类中员工属性,老师类中学生类数组属性
静态:
- 当想要共享时就用static
- 当想要方便调用时方法/属性时,加static
多态
- 前提:继承、重写、向上转型
- 运行效果:编译看父类,运行看子类
- 好处:扩展、灵活
- 多态使用场景(大装小,父类装子类)
- 场景1:方法的参数列表是父类类型,传参数传子类对象
- 场景2:父类数组,装子类对象
- 场景3:父类类型当方法的返回值类型,return 返回子类对象
2.2 场景2:
父类:
//形状 - -父类
public class Shape {
//面积方法
public double area(){
return 0.0;
}
//周长方法
public double girth(){
return 0.0;
}
}
子类:
//圆类
public class Circle extends Shape {
private double r;
public Circle(double r) {
this.r = r;
}
@Override
public double area() {
return 3.14 * r * r;
}
@Override
public double girth() {
return 2 * 3.14 * r;
}
}
//矩形类
public class Rect extends Shape {
private double l;//长
private double w;//宽
public Rect(double l, double w) {
this.l = l;
this.w = w;
}
@Override
public double area() {
return l * w;
}
@Override
public double girth() {
return 2 *(l+w);
}
}
//正方形类
public class Square extends Shape {
private double l;//边长
public Square(double l) {
this.l = l;
}
@Override
public double area() {
return l * l;
}
@Override
public double girth() {
return 4 * l;
}
}
测试类:
public static void main(String[] args) {
//创建一个长度为3的数组,且可以存储不同类型的对象
Shape[] shapeArr = new Shape[3];
//向上转型(圆形对象赋值给父类形状对象)
shapeArr[0] = new Circle(10);
shapeArr[1] = new Rect(10,10);
shapeArr[2] = new Square(10);
for (int i = 0; i < shapeArr.length; i++) {
System.out.println("形状的面积"+shapeArr[i].area());//能把方法放在输出语句中的,是因为方法有返回值
System.out.println("形状的周长"+shapeArr[i].girth());
}
}
结果:
三、抽象类(abstract)
抽象:抽象就是不具体
抽象类: 方法变的不具体
3.1 语法
/**
* Created by slc on 2023/10/12.
* 1.方法抽象是指方法没有方法体,还需要abstract修饰
* 2.抽象方法需要放在抽象类,使用abstract修饰类
* -- 解释1.父类定义了方法,子类也重写了父类方法,此时就不用父类方法了,干脆就直接定义为抽象方法
* 3.抽象类可以有正常方法
* 4.abstract 不能修饰属性
* 5.抽象类中可以有构造方法,但是不能直接new出来
* --即抽象方法所在的类不能被创建对象
* 6.抽象方法存在作用:是用来被子类继承的(重写)
*/
public abstract class Animal {
String name = "huahua";
public abstract void eat();
public abstract void play();
public Animal(){
System.out.println("Animal()");
}
}
/**
* Created by slc on 2023/10/12.
* 1.子类继承抽象类,二选一操作
* 1)要么本身定义为抽象类
* 2)要么实现所有的抽象方法(重写)
*/
public class Dog extends Animal{
public Dog(){
System.out.println("Dog()");
}
@Override
public void eat() {
}
@Override
public void play() {
}
}
测试类:
public class Test {
public static void main(String[] args) {
//抽象类不能实例化 -- 抽象类不能创建对象
//new Animal();
//创建子类对象 (继承了抽象类) --
Dog dog = new Dog();
System.out.println(dog.name);
System.out.println("-----------");
//抽象类虽然不创建对象,但是可以当作父类引用指向子类对象
//向上转型
Animal animal = new Dog();
animal.eat();
}
}
结果:
3.2 使用总结
抽象类应用就是一种约束的效果,是一种限制,子类一旦继承父类,就必须要重写方法
当设计程序时,有些类需要被子类继承且必须重写方法时,就把父类设计成抽象类
抽象类是用来抽象性差异。具体共同点的
也就是将差异性方法抽象化,提供给子类用于重写。共同的方法具体化,所有子类继承使用
把子类不一样的方法抽象成抽象方法,每个子类都重写
把子类都一样的方法提取到父类,每个子类直接用,不用重写。
四、 接口 (interface)
比抽象还抽象的Java文件就是 - 接口
4.1 特性
类就是一个模板,来描述一些统一的特性
接口不是类,只是一种定义,只是一种规范
引用数据类型:数组、类、接口
接口不是类,比抽象类还抽象的一种Java文件
接口不是类,使用interface所修饰
接口中没有属性,所有属性都是常量,默认被public static final所修饰
接口中不能定义普通方法,所有的方法都是抽象的,默认都是被public abstract所修饰
接口中没有构造方法,既不能创建对象
接口主要是用来被子类实现implements
子类实现接口,必须重写接口中的所有抽象方法,否则就要将自己定义为抽象类
子类允许多实现接口,中间用逗号隔开
子类在允许继承的同时,还可以实现接口
public class UDisk extends Electron implements USB,IA,Disk{}
接口之间允许继承,且允许多继承(类不允许多继承)
4.2 应用
与抽象类用处基本一致,多用在多态的场景
USB usb = new UDisk();
4.3 代码演示
U盘类:
/**
* Created by slc on 2023/10/13.
* - 子类实现接口,必须重写接口中的所有抽象方法,(否则就要将自己定义为抽象类)
* - 子类允许多实现接口,中间用逗号隔开
* - 子类在允许继承的同时,还可以实现接口
*/
//U盘类
public class UDisk extends Electron implements USB,Disk{
//重写Electron中的抽象方法
@Override
public void show() {
System.out.println("UDisk show()");
}
//重写USB接口中的抽象方法
@Override
public void trans() {
System.out.println("UDisk trans()");
}
//重写Disk接口中的抽象方法
@Override
public void save() {
System.out.println("UDisk save()");
}
}
抽象类(电子设备)
//电子设备
public abstract class Electron {
public abstract void show();
}
USB接口:
/**
* Created by slc on 2023/10/13.
* 接口不是类,是使用interface修饰
* <p>
* 接口可以继承接口,且是多继承
*/
public interface USB extends Disk {
/**
* 接口中属性默认被pubulic修饰 ,而且只能被public修饰
* 即接口中的属性都是 静态常量
* -- -static 是静态,可以直接通过类名.属性直接调用
*/
public static final int length = 5;
/**
* 接口中的方法全部
* 默认被public abstract所修饰
* 即所有方法都是抽象类
*/
public abstract void trans();
/**
* 接口中没有构造方法
* --- 即不允许创建构造方法
*/
// public USB(){};
}
接口:
//接口
public interface Disk {
void save();
}
测试类:
public class Test {
public static void main(String[] args) {
//创建对象
//USB usb = new USB();//因为USB是abstract,无法实例化,既无法创建对象
//向上引用 -- 即父类引用指向子类对象
USB usb = new UDisk();
usb.trans();
}
}
输出结果:
4.4 继承与实现的经验
对于以后写代码,该怎么设计?
- 很多类有公共的属性,方法抽取成父类来继承
- 有些类中的方法被抽取到父类中,但是子类又不合适还需要重写,那就把类和方法定义成抽象
- 将一些功能性的行为定义成接口,让想拥有这些功能的类去实现即可
五、内部类
5.1 概述
内部类的概念:
在一个类中定义的类,称之为内部类,外面的类称为外部类
内部类的分类:
- 1.成员内部类
- 2.静态内部类
- 3.局部内部类
- 4.匿名内部类
内部类的特点:
- 1.内部类可以访问到外部类的私有成员,且不破坏封装性
- 2.内部类也会生成.class文件.名为:外部类名$内部类名.class
外部类的特点:
- 1.一个Java文件中可以编写多个类,但是只能有一个类能使用public修饰,称之为主类。主类名必须与文件名一致
- 建议:以后开发中,一个Java文件就编写一个类
5.2 示例:
public class Outer {
private int outer = 1;
void testOuter() {
//外部类访问不到内部的属性与方法
//System.out.println(inner);//访问不到
}
//成员内部类
public class Inner {
int inner = 2;
void testInner() {
//内部类可以用外部变量与方法
System.out.println(outer);
testOuter();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建Outer类
Outer outer = new Outer();
outer.testOuter();
//创建内部内对象
Outer.Inner inner = new Outer().new Inner();
}
}
5.3 匿名内部类
语法:
new 父类/接口(){
重写接口类的方法
}
实例:
USB接口:
//USB接口
public interface USB {
void trans();
}
测试类:
public class TestUSB {
public static void main(String[] args) {
/**
* 按以前,需要新建一个类实现USB接口,创建子类实现对象
* 但是如果该接口的方法只需要实现一次,那么这么做就浪费时间
*
* 可以使用匿名内部类来简写
* 匿名内部类语法:
* new 接口名(){
* 重写接口类中方法()
* }
* 相对于: 实现了接口并重写了方法且创建了子类对象
*/
//匿名内部类
new USB() {
@Override
public void trans() {
System.out.println("传输");
}
}.trans();//传输
System.out.println("-----------");
USB usb = new USB() {//引入局部变量
@Override
public void trans() {
System.out.println("传输");
}
};
usb.trans();//传输
}
}
特点:
- 1.匿名内部类本身就是一个对象
- 2.一般匿名内部类中都不会定义属性和方法,因为没有意义
- 3.匿名内部类的父类一般都是抽象类或者是接口
匿名内部类的应用场景
- 1.如果一个方法的参数是接口,且这个接口只需要实现一次,那么就可以使用匿名内部类
示例:
public static void main(String[] args) {
/**
* 按以前,需要新建一个类实现USB接口,创建子类实现对象
* 但是如果该接口的方法只需要实现一次,那么这么做就浪费时间
*
* 可以使用匿名内部类来简写
* 匿名内部类语法:
* new 接口名(){
* 重写接口类中方法()
* }
* 相对于: 实现了接口并重写了方法且创建了子类对象
*/
//匿名内部类
new USB() {
@Override
public void trans() {
System.out.println("传输");
}
}.trans();//传输
System.out.println("-----------");
USB usb = new USB() {//引入局部变量
@Override
public void trans() {
System.out.println("传输");
}
};
usb.trans();//传输
System.out.println("--------");
//调用接口参数方法
addUSB(new USB() {//lambda表达式
@Override
public void trans() {
System.out.println("chuanshu");
}
});
}
//方法传入接口参数
public static void addUSB(USB usb) {
usb.trans();
}
}
六、抽象类 && 接口对比
抽象 | 接口 | |
---|---|---|
修饰符 | abstract | interface |
类 | 是类 | 不是类 |
属性 | 正常属性 | 全是静态常量 |
方法 | 可以定义普通方法,抽象方法 | 全部都是抽象方法 |
构造方法 | 有构造方法 | 没有构造方法 |
子类 | 继承抽象类,单继承 | 实现接口,多实现 |
应用 | 多个类有共同特点,行为时。向上抽取形成父类,如果子类都会重写的方法,可以定义为抽象 | 接口一般用来定义功能,行为 |
-------- | ------------------------------------------------------------ | -------------------------- |
| 修饰符 | abstract | interface |
| 类 | 是类 | 不是类 |
| 属性 | 正常属性 | 全是静态常量 |
| 方法 | 可以定义普通方法,抽象方法 | 全部都是抽象方法 |
| 构造方法 | 有构造方法 | 没有构造方法 |
| 子类 | 继承抽象类,单继承 | 实现接口,多实现 |
| 应用 | 多个类有共同特点,行为时。向上抽取形成父类,如果子类都会重写的方法,可以定义为抽象 | 接口一般用来定义功能,行为 |