03-多态、内部类、匿名内部类、枚举、Object、Objects
1、多态
1.1、概念
- 一类事物的多种形态
- H2O
- 常温:水
- 低温:冰
- 高温:水蒸气
1.2、格式
- 父类引用指向子类对象
- 父类 变量名 = new 子类();
- Animal a = new Dog();
- Animal a = new Cat();
- 接口 变量名 = new 实现类();
1.3、多态的前提
-
- 有继承或者实现关系
- 要有方法重写
1.4、多态的作用
左边写父类或者接口,右边可以是任意子类,提高代码扩展性
eg:Animal a = new Dog();/Animal a = new Cat();
1.5、多态的特点
编译看左边,运行看右边
1.6、多态的弊端
- 不能使用子类特有方法
- eg:Animal a = new Dog();
- // a.lookhome();
1.7、示例代码
public class Demo01 { public static void main(String[] args) { // 以前我们创建对象: 类名 变量名 = new 类名(); Cat c = new Cat(); Dog d = new Dog(); d.eat(); // 多态: 父类/接口 变量名 = new 子类/实现类(); // 多态的好处:右边可以创建任意子类,方便切换,增加了扩展性 Animal a = new Dog(); a.eat(); // 多态时执行右边对象的方法 // a.lookHome(); // 多态:编译看左边,运行看右边 // 多态真正的好处是在调用方法时使用多态 goEat(d); goEat(c); } // 定义一个方法,假设这个方法是你同事写好的,你没有资格改 // 请动物吃饭 // 方法参数写父类,可以传入任意子类对象 // goEat(d); Animal d = new Dog(); // goEat(c); Animal d = new Cat(); public static void goEat(Animal d) { d.eat(); } // 如果方法参数写一个具体子类,那么只能传入这一种类型.需要写多个重载的方法 /* public static void goEat(Dog d) { d.eat(); } public static void goEat(Cat d) { d.eat(); }*/ }
1.8、 引用类型转换
1.8.1、 向上转型
将子类转成父类(多态)
eg: Animal a = new Dog();
Animal a = new Cat();
1.8.2、向下转型
将父类转成子类 子类 变量名 = (子类) 父类对象;
向下转型的好处:可以调用子类的特有方法
eg:
Animal a = new Dog();
Dog d = (Dog)a;
d.lookHome(); // 调用子类的特有方法
1.8.3、instanceof关键字
- 格式:变量名 instanceof 类名
- 作用:判断变量是否是这种类型
1.8.4 示例代码
public class Demo021 { public static void main(String[] args) { // 向上转型(多态): 将子类转成父类 父类 变量名 = new 子类(); Animal a = new Dog(); // Animal a = new Cat(); a.eat(); // a.lookHome(); // 不能调用子类特有方法 boolean b = a instanceof Dog; System.out.println("b = " + b); // 提高程序的健壮性,让程序不容易崩溃 if (b) { // 将a对象由Animal类型转成Dog类型 Dog d = (Dog)a; // ClassCastException: 类型转换异常 d.lookHome(); // 调用子类的特有方法 } else { System.out.println("不是一只狗"); } } }
2、内部类(了解)
2.1、概念
把一个类定义到另一个类的里面(类种类)
- eg:
class Cc { // 外部类(宿主) class Dd { // 内部类(寄生) } }
2.2、内部类的分类
- 按照定义的位置
- 成员内部类
- 局部内部类
2.3、什么使用内部类
- 一个事物内部还有一个事物,内部的事物脱离外部的事物无法独立运行
- eg:人里面有一颗心脏
2.4、成员内部类
2.4.1、概念
在类中定义且在类中方法外的类
2.4.2、成员内部类的使用
- 创建外部类对象
- 通过外部类对象创建内部类都西昂
2.4.3、成员内部类的好处
可以直接使用外部类的成员(成员变量,成员方法)
2.4.4、内部类编译后的结果
- 内部类名$内部类名.class
- Body$Heart.class
2.4.5、示例代码
Body类
public class Body { public boolean isLive = true; // 是否活着 public void walk() { System.out.println("人在散步"); } // 成员位置: 类中方法外 这里定义一个类(成员内部类) class Heart { private String color = "红色"; public void jump() { if (isLive) { System.out.println("心脏跳动一下!"); } else { System.out.println("心脏停止跳动,凉凉!"); } } } }
测试类
public class Demo05 { public static void main(String[] args) { // 1.创建外部类对象 Body body = new Body(); // 2.通过外部类对象创建内部类对象 Body.Heart heart = body.new Heart(); // 有些地方是这么创建内部的 (不建议,没有保存外部类对象) // Body.Heart heart2 = new Body().new Heart(); // 3.调用内部类方法 heart.jump(); body.isLive = false; heart.jump(); } }
2.5、静态成员内部类
2.5.1、概念
在成员内部类的前面添加static修饰
2.5.2、静态成员内部类特点
静态成员内部类只能使用外部类静态修饰的成员
2.5.3、静态成员内部类的创建格式
- 直接创建静态内部类
- 外部类名.内部类名 变量名 = new 外部类名.内部类名();
- eg:Body.Heart heart = new Body.Heart();
2.5.4、示例代码
Body类
public class Body { public static boolean isLive = true; // 是否活着 public void walk() { System.out.println("人在散步"); } // 静态成员内部类 static class Heart { private String color = "红色"; public void jump() { if (isLive) { System.out.println("心脏跳动一下!"); } else { System.out.println("心脏停止跳动,凉凉!"); } } } }
测试类
public class Demo07 { public static void main(String[] args) { Body.Heart heart = new Body.Heart(); heart.jump(); } }
2.6 局部内部类
2.6.1、概念
定义在方法中的类
2.6.2、局部内部类的使用
在定义局部内部类的下方创建对象去使用
2.6.3、局部内部类的好处
局部内部类可以直接使用外部类的成员
2.6.4、局部内部类编译后的特点
- 外部类名$数字内部类名.class
- Chinese$1Chopsticks.class
2.6.5、示例代码
Chinese类
// 中国人 public class Chinese { public String color = "黄"; public void eat() { // 在方法中定义筷子类 class Chopsticks { public int length = 60; // 使用筷子 public void use() { System.out.println(color + "颜色的人,使用长度为 " + length + " 的筷子吃饭"); } } // 创建局部内部类对象 Chopsticks chopsticks = new Chopsticks(); chopsticks.use(); } }
测试类
public class Demo08 { public static void main(String[] args) { Chinese chinese = new Chinese(); chinese.eat(); } }
3、匿名内部类
3.1、概念
匿名内部类就是没有名字的内部类
3.2、格式
父类/接口 对象名 = new 父类/接口(){ 重写方法 }
3.3、匿名内部类的好处
简化代码
3.4、匿名内部类的本质
省去了创建子类或者实现类再调用子类和实现类的重写方法步骤,而是直接创建子类或者实现类然后直接使用括号进行重写
3.5、示例代码
public class Demo09 { public static void main(String[] args) { // 3.使用实现类 Student s = new Student(); s.swimming(); // 匿名内部类 // 接口 变量名 = new 接口的实现类(); Swimmable sw = new Swimmable() { @Override public void swimming() { System.out.println("我是匿名内部类游泳"); } }; sw.swimming(); } }
3.6、匿名内部类的使用场景
-
方法参数要接口传入匿名内部类,简化代码
-
public class Demo10 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(6); list.add(8); list.add(1); list.add(3); // 体验匿名内部类简化代码 // 传入匿名内部类, 使用简单,方便观看 Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); System.out.println(list); } }
4、枚举
4.1、不适用枚举存在的问题
不适用枚举,性别可以随便写,造成非法的数据
-
eg:
-
public class Person { private String name; private String sex; // 男, 女 } Person p = new Person("凤姐", "呵呵");
-
4.2、概念
- 一个一个地列举出来。如列举某个类型的所有值
eg:
- 性别只有男和女
- 一个星期只有7天
- 适合类型只有几个固定值的情况
4.3、格式
-
enum 枚举名 { 成员变量名1, 成员变量名2, 成员变量名3; } -
枚举中的成员变量名也称为“枚举项”
-
public enum Gender {
MAN,WOMEN;
}
4.4、使用方式
- 枚举名.成员变量名
- Person person = new Person("凤姐", Gender.MAN);
4.5、使用枚举的好处
将来不能随便写数据,一定是从枚举中选择某一项
4.6、枚举的应用场景
- 方向
- 季节
- 月份
- 小时
4.7、枚举深入了解
4.7.1、反编译工具介绍
- xxx.java -> 编译javac -> xxx.class
- xxx.class -> 反编译工具 -> xxx.java
- enum是一个关键字,我们看不到它的源码,可以使用反编译工具
4.7.2、季节枚举示例
public enum Season { SPRING, SUMMER, AUTUMN, WINTER; }
4.7.3、反编译上一个示例
final class Season1 extends Enum { public static final Season SPRING = new Season("SPRING", 0); public static final Season SUMMER = new Season("SUMMER", 1); public static final Season AUTUMN = new Season("AUTUMN", 2); public static final Season WINTER = new Season("WINTER", 3); private Season1(String s, int i) { super(s, i); } }
5、Object类
5.1、Object类的特点
- Object类是所有类的父类(祖宗),所有类都直接或间接地继承了Object类
5.2、toString方法
- Object类中默认的toString方法返回 包名.类名@地址
- com.itheima.demo15toString方法_重点.Person@4554617c
- 当我们觉得打印对象的地址没有什么意义的时候,子类可以重写父类的toString方法,打印对象的成员变量
- 使用快捷键:alt + insert -> toString()
5.3、示例代码
Person类
public class Person { private String name; private int age; // 子类觉得父类方法不能满足要求,子类可以重写父类方法 public String toString() { return "Person{name = " + name + ", age = " + age + "}"; }}
测试类
Person p1 = new Person("柳岩", 18); String str = p1.toString(); // com.itheima.demo15toString方法_重点.Person@4554617c System.out.println(str); // 打印对象 System.out.println(p1.toString()); System.out.println(p1); // 打印对象,就是调用对象的toString进行打印
5.4、equals方法
-
回顾 "=="符号
-
基本数据类型比较数据的值是否相等
- System.out.println(3 == 5)//falase
-
引用数据类型比较对象的地址是否相等
-
String str1 = new String("abc"); String str2 = new String("abc"); System.out.println(str1 == str2);
-
-
-
Object类中的equals默认比较对象的地址
-
当我们不像比较对象的地址,而是对象的成员变量时
- 重写equals方法
- 快捷键:alt + insert - > equals() and hashCode()
5.5、示例代码
Student类
public class Student { private String name; private int age; @Override public boolean equals(Object o) { // 如果是同一个对象,返回true if (this == o) return true; // 如果o为null,或者两者类型不同返回false if (o == null || getClass() != o.getClass()) return false; // 能到下面来说明类型相同,强转 Student student = (Student) o; // 年龄不同返回false if (age != student.age) return false; // 如果姓名相同返回true,姓名不同返回false return name != null ? name.equals(student.name) : student.name == null; }
测试类
public class Demo16 { public static void main(String[] args) { System.out.println(3 == 5); // false Student s1 = new Student("小张", 12); Student s2 = new Student("老张", 12); System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); } }
6、Objects类
6.1 Objects介绍
- 是JDK1.7提供的类,其中有很多静态方法帮助我们操作对象
- 一般来说类名后面带s的都是工具类,里面会提供一些静态方法,帮助我们操作某些对象
6.2 常用方法
-
public static boolean equals(Object a, Object b) // 判断a和b是否相同,本质a.equals(b) -
public static boolean isNull(Object obj) // 判断是否为null,如果为null返回true
6.3、示例代码
public class Demo17 { public static void main(String[] args) { // Student s1 = new Student("花花", 20); Student s1 = null; Student s2= new Student("花花", 20); // null调用方法会出现空指针异常 // System.out.println(s1.equals(s2)); // JDK1.7时增加Objects类,里面有一个equals,帮我们做更严谨的判断 System.out.println(Objects.equals(s1, s2)); /* Objects类中的 public static boolean equals(Object a, Object b) { // a = s1; // b = s2; // a != null && a.equals(b): 先判断a!null才去调用a.equals(b) return (a == b) || (a != null && a.equals(b)); } */ // public static boolean isNull(Object obj) 判断是否为null,如果为null返回true System.out.println(Objects.isNull(s1)); // true System.out.println(Objects.isNull(s2)); // false } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?