Java基础13
内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为 内部类 ,类B则称为 外部类。
内部类的声明理由:
当一个事物A的内部,有一部分需要一个完整的结构B进行描述,而这个内部结构B只为外部事物A提供服务,并不会在其他地方用到,那么整个内部的完整结构B最好使用内部类。
遵循 高内聚、低耦合 的面向对象开发原则。
举例: Thread类内部声明了State类,表示线程的生命周期。
HashMap类中声明了Node类,表示封装的key和Value
分类:(参考变量的分类)
> 成员内部类:直接声明在外部类的里面
> 使用static修饰的: 静态的成员内部类
> 不使用static修饰的:非静态的成员内部类
> 局部内部类:声明在方法内、构造器内、代码块内的内部类
> 匿名的局部内部类
> 非匿名的局部内部类
class Person{ // 外部类 // 静态的成员内部类 static class Dog{ } // 非静态的成员内部类 class Bird{ } public void method(){ // 局部内部类 class InnerClass1{ } } public Person(){ // 局部内部类 class InnerClass1{ } } { // 局部内部类 class InnerClass1{ } } }
成员内部类的理解
> 从类的角度看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
> 从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
- 除了使用public、缺省权限修饰之外,还可以用private、protected修饰
- 可以使用static修饰
创建成员内部类的实例
public class OuterClassTest { public static void main(String[] args) { //1.创建Person的静态的成员内部类的实例 Person.Dog dog = new Person.Dog(); dog.eat(); //2.创建Person的非静态的成员内部类的实例 // Person.Bird bird = new Person.Bird(); 错误写法 Person p1 = new Person(); Person.Bird bird = p1.new Bird(); bird.eat(); } } class Person{ // 外部类 // 静态的成员内部类 static class Dog{ public void eat(){ System.out.println("吃骨头!"); } } // 非静态的成员内部类 class Bird{ public void eat(){ System.out.println("鸟吃虫!"); } } }
在成员内部类中调用外部类的结构
public class OuterClassTest { public static void main(String[] args) { //1.创建Person的静态的成员内部类的实例 Person.Dog dog = new Person.Dog(); dog.eat(); //2.创建Person的非静态的成员内部类的实例 // Person.Bird bird = new Person.Bird(); 错误写法 Person p1 = new Person(); Person.Bird bird = p1.new Bird(); bird.eat(); bird.show("黄鹂"); bird.show1(); } } class Person{ // 外部类 String name = "Tom"; int age = 20; // 静态的成员内部类 static class Dog{ public void eat(){ System.out.println("吃骨头!"); } } // 非静态的成员内部类 class Bird{ String name = "啄木鸟"; public void eat(){ System.out.println("鸟吃虫!"); } public void show(String name){ System.out.println("age = " + age); // 20 System.out.println("name = " +name); // 黄鹂 show 方法的形参 System.out.println("name = " + this.name); // 啄木鸟 (谁调用这个方法就是谁) System.out.println("name = " + Person.this.name); // Tom } public void show1 (){ eat(); // 鸟吃虫! this.eat(); // 鸟吃虫! Person.this.eat(); // 人吃饭! } } public void eat(){ System.out.println("人吃饭!"); } }
局部内部类的使用
public class OuterClassTest { // 说明:局部内部类的使用 public void method1(){ // 局部内部类 class A{ // 可以声明属性、方法等 } } //开发中的场景 public Comparable getInstance(){ //返回一个实现了Comparable的对象 // 提供了实现Comparable接口的类 // 方式1: 提供了接口的实现类的匿名对象 class MyComparable implements Comparable{ @Override public int compareTo(Object o) { return 0; } } return new MyComparable(); } public Comparable getInstance1(){ // 方式2:提供了接口的匿名实现类的对象 Comparable c = new Comparable() { @Override public int compareTo(Object o) { return 0; } }; return c; } public Comparable getInstance2(){ // 方式3:提供了接口的匿名实现类的匿名对象 return new Comparable() { @Override public int compareTo(Object o) { return 0; } }; } }
练习
编写一个匿名内部类,它继承Object,并在匿名内部类中声明一个方法public void test( )打印尚硅谷。
public class InnerTest { public static void main(String[] args) { new Object(){ // 这里实际上是一个继承了Object类匿名实现类的匿名对象 public void test(){ System.out.println("你好!"); } }.test(); // 在这里直接调用了匿名对象的方法 } }
匿名对象实现的几种举例
public class OuterClassTest { public static void main(String[] args) { // 传统方法 SubA a = new SubA(); a.method(); // 举例1 A a1 = new A() { // 提供接口匿名实现类的对象 @Override public void method() { System.out.println("匿名实现类重写的方法"); } }; a1.method(); // 举例2 new A(){ // 提供接口匿名实现类的匿名对象 @Override public void method() { System.out.println("匿名实现类重写的方法"); } }.method(); //传统方法 SubB b = new SubB(); b.method1(); //方法1: B b1 = new B() { @Override public void method1() { System.out.println("继承于抽象类的子类调用的方法"); } }; b1.method1(); // 方法2 new B(){ @Override public void method1() { System.out.println("继承于抽象类的子类调用的方法"); } }.method1(); //传统方法 C c = new C(); c.method2(); //方法1: C c1 = new C(){}; // 提供了一个继承于C的匿名子类的对象 这里花括号里面没写 因为不是继承的抽象类 所以没有必须要重写的方法 c1.method2(); } } interface A{ public void method(); } // 传统做法 class SubA implements A{ @Override public void method() { System.out.println("SubA!"); } } abstract class B{ public abstract void method1(); } class SubB extends B{ @Override public void method1() { System.out.println("SubB!"); } } class C{ public void method2(){ System.out.println("C"); } }
枚举类
枚举类的理解:
枚举类本质上也是一种类,只不过这个类的对象是有限的、固定的几个,不能让用户随意创建。
举例:
- ‘星期’ : Monday、......、Sunday
- '性别':Man、Woman
可以直接用类调用对象
开发中的建议:
> 开发中,如果针对于某种类,其实例是确定个数的。则推荐将此类声明为枚举类。
> 如果枚举类的实例只有一个,则可以看作是单例的实现方式。
jdk5.0之前定义枚举类(了解)
public class SeasonTest { public static void main(String[] args) { System.out.println(Season.SPRING); System.out.println(Season.SUMMER.getSeasonDesc()); } } // jdk5.0之前的定义枚举类的方式 class Season{ // 2.声明当前类的对象的实例变量 需要使用private final修饰 private final String seasonName; // 季节的名称 private final String seasonDesc; // 季节的描述 // 1. 私有化类的构造器 private Season(String seasonName, String seasonDesc) { // final修饰的必须在构造器、代码块等地方初始化 this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 3. 提供实例变量的get方法 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } //4. 创建当前类的实例 需要使用public static final修饰 public static final Season SPRING = new Season("春天","春暖花开"); public static final Season SUMMER = new Season("夏天","夏日炎炎"); public static final Season AUTUMN = new Season("秋天","秋高气爽"); public static final Season WINTER = new Season("冬天","白雪皑皑"); @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; } }
jdk5.0中使用enum定义枚举类
public class SeasonTest1 { public static void main(String[] args) { System.out.println(Season1.AUTUMN); System.out.println(Season1.SPRING.getSeasonDesc()); } } // jdk5.0中使用enum关键字定义枚举类 enum Season1{ //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开 SPRING("春天","春暖花开"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); // 2.声明当前类的对象的实例变量 需要使用private final修饰 private final String seasonName; // 季节的名称 private final String seasonDesc; // 季节的描述 // 1. 私有化类的构造器 private Season1(String seasonName, String seasonDesc) { // final修饰的必须在构造器、代码块等地方初始化 this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 3. 提供实例变量的get方法 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } }
Enum中的常用方法
使用enum关键字定义的枚举类,默认其父类是java.lang.Enum类
使用enum关键字定义的枚举类,不要再显示的定义其父类,否则报错。
常用方法
String toString():默认返回的是常量名(对象名),可以继续手动重写该方法。
(重点)static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法。
(重点)static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象,要求字符串必须是枚举类对象的实例。
String name():得到当前枚举常量的名字。建议优先使用toString()。
int ordinal():返回当前枚举常量的次序号,默认从0开始。
public class SeasonTest1 { public static void main(String[] args) { //测试方法 // 1. toString() System.out.println(Season1.SPRING); //如果没有重写toString方法,默认打印名称(SPRING),可以重写toString方法更改 // 2. values() 可以查看枚举类的对象有哪些 Season1[] values = Season1.values(); for(int i = 0; i < values.length; i ++){ System.out.println(values[i]); } // 3. valueOf(String objName): 返回当前枚举类中名称为objName的枚举类对象 // 如果枚举类中不存在为objName名称的对象,则报错! String objName = "WINTER"; Season1 season1 = Season1.valueOf(objName); System.out.println(season1); // 4. name() System.out.println(Season1.SPRING.name()); // 默认打印对象的名字 SPRING // 5. ordinal() 打印对象所在的列表的位置 System.out.println(Season1.AUTUMN.ordinal()); } } // jdk5.0中使用enum关键字定义枚举类 enum Season1{ //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开 SPRING("春天","春暖花开"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); // 2.声明当前类的对象的实例变量 需要使用private final修饰 private final String seasonName; // 季节的名称 private final String seasonDesc; // 季节的描述 // 3. 私有化类的构造器 private Season1(String seasonName, String seasonDesc) { // final修饰的必须在构造器、代码块等地方初始化 this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 4. 提供实例变量的get方法 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } }
枚举类实现接口的操作
情况1: 枚举类实现接口,在枚举类中重写接口的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法
情况2: 让枚举类的每一个对象重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是不同的实现的方法。
- 情况1
public class SeasonTest1 { public static void main(String[] args) { // 通过枚举类的对象调用接口中重写的方法 Season1.AUTUMN.show(); } } interface Info{ void show(); } // jdk5.0中使用enum关键字定义枚举类 enum Season1 implements Info{ //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开 SPRING("春天","春暖花开"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","白雪皑皑"); // 2.声明当前类的对象的实例变量 需要使用private final修饰 private final String seasonName; // 季节的名称 private final String seasonDesc; // 季节的描述 // 3. 私有化类的构造器 private Season1(String seasonName, String seasonDesc) { // final修饰的必须在构造器、代码块等地方初始化 this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 4. 提供实例变量的get方法 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } @Override // 重写接口中的方法 public void show() { System.out.println("这是一个季节"); } }
- 情况2
public class SeasonTest2 { public static void main(String[] args) { Season2[] season2 = Season2.values(); for (int i = 0; i < season2.length; i ++){ season2[i].show(); } } } interface Info1{ void show(); } // jdk5.0中使用enum关键字定义枚举类 enum Season2 implements Info{ //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开 SPRING("春天","春暖花开"){ @Override public void show() { System.out.println("春天在哪里?"); } }, SUMMER("夏天","夏日炎炎"){ @Override public void show() { System.out.println("宁静的夏天!"); } }, AUTUMN("秋天","秋高气爽"){ @Override public void show() { System.out.println("秋意浓!"); } }, WINTER("冬天","白雪皑皑"){ @Override public void show() { System.out.println("大约在冬季"); } }; // 2.声明当前类的对象的实例变量 需要使用private final修饰 private final String seasonName; // 季节的名称 private final String seasonDesc; // 季节的描述 // 3. 私有化类的构造器 private Season2(String seasonName, String seasonDesc) { // final修饰的必须在构造器、代码块等地方初始化 this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 4. 提供实例变量的get方法 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } }
使用枚举类实现单例模式
public class BankTest1 { public static void main(String[] args) { System.out.println(Bank2.CPB); } } // jdk5.0之前的使用枚举类定义单例模式 class Bank1{ private Bank1() { } private static final Bank1 instance = new Bank1(); } // jdk5.0中使用enum关键字定义枚举类的方式定义单例模式 enum Bank{ CPB; // 没有具体对象的实例变量 } //上面类 有具体对象的实例变量 enum Bank2{ CPB("BeiJing"); // 传统的写法是:public static final Bank2 CPB = new Bank2("BeiJing"); private final String site; private Bank2(String site){ this.site = site; } }
练习
声明final修饰的int类型的属性red,green,blue
声明final修饰的String类型的属性description
声明有参构造器Color(int red,int green,int blue,String description)
创建7个常量对象:红、橙、黄、绿、青、蓝、紫
重写toString方法,例如:RED(255,0,0)->红色
(2)在测试类中,使用枚举类,获取绿色对象,并打印对象
提示:
-7个常量对象的RGB值如下
红:(255,0 0)
橙:(255,128,0)
黄:(255,255,0)
绿:(0,255,0)
青:(0,255,255)
蓝:(0,0,255)
紫:(128,0,255)
7个常量对象名如下:
RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE
public class ColorTest { public static void main(String[] args) { Color[] colors = Color.values(); for (int i = 0; i < colors.length; i++){ System.out.println(colors[i]); } System.out.println(Color.YELLOW.name()); } } enum Color{ RED(255,0,0,"红色"),ORANGE(255,128,0,"橙色"),YELLOW(255,255,0,"黄色"),GREEN(0,255,0,"绿色"), CYAN(0,255,255,"青色"),BLUE(0,0,255,"蓝色"),PURPLE(128,0,255,"紫色"); private final int red; private final int blue; private final int green; private final String description; Color(int red,int blue,int green,String description) { this.red = red; this.blue = blue; this.green = green; this.description = description; } @Override public String toString() { return name() + "(" + this.red + "," + this.blue + "," + this.green + ") ->" + this.description; // 或者写return super.toString() + "(" + this.red + "," + this.blue + "," + this.green + ") ->" + this.description; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通