42
42-面对对象编程(高级部分)2
- 接口
- 快速入门
- 基本介绍
- 接口应用场景
- 接口注意事项
- 课堂练习
- 接口VS继承
- 实现接口VS继承类
- 接口的多态特性
- 课堂练习
- 内部类
- 基本介绍
- 基本语法
- 内部类的分类
- 局部内部类
- 匿名内部类(重点)
- 匿名内部类实践
- 成员内部类
- 静态内部类
- 枚举
- 枚举的引出
- 枚举的两种实现方法
- 自定义类实现枚举
- enum关键字实现枚举
- enum练习
- enum成员方法
- enum课堂练习
- enum实现接口
- 注解
- 注解的理解
- 基本的Annotation案例
- @Override 注解
- @Ddeprecated 注解
- @SuppressWarnings 注解
- 元注解
- 基本介绍
- 元注解的种类
- @Retention 注解
- @Target 注解
- @Documented 注解
- @Inherited 注解
- 章节作业
- 最后
接口
快速入门
package com.hspedu.interface_;public class Interface01 {public static void main(String[] args) {//创建手机,相机对象//Camera 实现了 UsbInterfaceCamera camera = new Camera();//Phone 实现了 UsbInterfacePhone phone = new Phone();//创建计算机Computer computer = new Computer();computer.work(phone);//把手机接入到计算机System.out.println("===============");computer.work(camera);//把相机接入到计算机}
}
package com.hspedu.interface_;public class Computer {//编写一个方法, 计算机工作//解读://1. UsbInterface usbInterface 形参是接口类型 UsbInterface//2. 看到 接收 实现了 UsbInterface接口的类的对象实例public void work(UsbInterface usbInterface) {//通过接口,来调用方法usbInterface.start();usbInterface.stop();}
}
package com.hspedu.interface_;public interface UsbInterface { //接口//规定接口的相关方法,老师规定的.即规范...public void start();public void stop();
}
package com.hspedu.interface_;public class Camera implements UsbInterface{//实现接口,就是把接口方法实现@Overridepublic void start() {System.out.println("相机开始工作...");}@Overridepublic void stop() {System.out.println("相机停止工作....");}
}
package com.hspedu.interface_;public class Computer {//编写一个方法, 计算机工作//解读://1. UsbInterface usbInterface 形参是接口类型 UsbInterface//2. 看到 接收 实现了 UsbInterface接口的类的对象实例public void work(UsbInterface usbInterface) {//通过接口,来调用方法usbInterface.start();usbInterface.stop();}
}
基本介绍
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来
- 语法:
interface 接口名(){//属性//方法(1,抽象方法,2.默认实现方法(default),3.静态方法)
}class 类名 implements 接口{//自己属性//自己方法(必须实现的接口的抽象方法)
}
- 小结:1.在jdk7.0前,接口的所有方法都没有方法体,即都是抽象方法。 2. jdk8.0后可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
package com.hspedu.interface_;public class Interface02 {public static void main(String[] args) {}
}//老韩解读
//1.如果一个类 implements实现 接口
//2. 需要将该接口的所有抽象方法都实现
class A implements AInterface {@Overridepublic void hi() {System.out.println("hi()....");}
}
package com.hspedu.interface_;public interface AInterface {//写属性public int n1 = 10;//写方法//在接口中,抽象方法,可以省略abstract关键字public void hi();//在jdk8后,可以有默认实现方法,需要使用default关键字修饰default public void ok() {System.out.println("ok ...");}//在jdk8后, 可以有静态方法public static void cry() {System.out.println("cry ....");}
}
接口应用场景
- 可以约束命名
- 调用时向上转型,动态绑定
package com.hspedu.interface_;public class Interface03 {public static void main(String[] args) {MysqlDB mysqlDB = new MysqlDB();t(mysqlDB);OracleDB oracleDB = new OracleDB();t(oracleDB);}public static void t(DBInterface db) {db.connect();db.close();}
}
package com.hspedu.interface_;public interface DBInterface { //项目经理public void connect();//连接方法public void close();//关闭连接
}
package com.hspedu.interface_;
//A程序
public class MysqlDB implements DBInterface {@Overridepublic void connect() {System.out.println("连接mysql");}@Overridepublic void close() {System.out.println("关闭mysql");}
}
package com.hspedu.interface_;//B程序员连接Oracle
public class OracleDB implements DBInterface{@Overridepublic void connect() {System.out.println("连接oracle");}@Overridepublic void close() {System.out.println("关闭oracle");}
}
接口注意事项
- 接口不能被实例化
- 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
- 一个普通实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口方法
public class InterfaceDetail01 {public static void main(String[] args) {//new IA();}
}//1.接口不能被实例化
//2.接口中所有的方法是 public方法, 接口中抽象方法,可以不用abstract 修饰
//3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决
//4.抽象类去实现接口时,可以不实现接口的抽象方法
interface IA {void say();//修饰符 public protected 默认 privatevoid hi();
}
class Cat implements IA{@Overridepublic void say() {}@Overridepublic void hi() {}
}
abstract class Tiger implements IA {}
- 一个类同时可以实现多个接口
- 接口中属性。只能是final的,而且是public static final修饰符,比如:int a = 1; 实际上是public static final int a = 1;(必须初始化)
- 接口中属性的访问形式:接口名.属性名
- 一个接口不能继承其他的类,但是可以继承多个别的接口
eg: interface A extends B,C{} - 接口的修饰符只能是public和默认,这点和类的修饰符是一样的
package com.hspedu.interface_;public class InterfaceDetail02 {public static void main(String[] args) {//老韩证明 接口中的属性,是 public static finalSystem.out.println(IB.n1);//说明n1 就是static//IB.n1 = 30; 说明n1 是 final}
}
interface IB {//接口中的属性,只能是final的,而且是 public static final 修饰符int n1 = 10; //等价 public static final int n1 = 10;void hi();
}
interface IC {void say();
}
//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
}
//接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE{}//一个类同时可以实现多个接口
class Pig implements IB,IC {@Overridepublic void hi() {}@Overridepublic void say() {}
}
课堂练习
接口VS继承
package com.hspedu.interface_;public class ExtendsVsInterface {public static void main(String[] args) {LittleMonkey wuKong = new LittleMonkey("悟空");wuKong.climbing();wuKong.swimming();wuKong.flying();}
}//猴子
class Monkey {private String name;public Monkey(String name) {this.name = name;}public void climbing() {System.out.println(name + " 会爬树...");}public String getName() {return name;}
}//接口
interface Fishable {void swimming();
}
interface Birdable {void flying();
}//继承
//小结: 当子类继承了父类,就自动的拥有父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式扩展.
// 可以理解 实现接口 是 对java 单继承机制的一种补充.
class LittleMonkey extends Monkey implements Fishable,Birdable {public LittleMonkey(String name) {super(name);}@Overridepublic void swimming() {System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");}@Overridepublic void flying() {System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");}
}
实现接口VS继承类
- 接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值在于:设计号各种规范(方法),让其他类去实现这些方法,即更加的灵活 - 接口比继承更加灵活
接口比继承更加灵活,继承是满足is-a的关系,而接口只需满足like-a关系 - 接口在一定程度上实现代码解耦【即:接口规范性+动态绑定】
接口的多态特性
- 多态参数(前面案例体现)
在前面的USB接口案例,Usb usb,即可以接受手机对象,又可以接受相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)
package com.hspedu.interface_;public class InterfacePolyParameter {public static void main(String[] args) {//接口的多态体现//接口类型的变量 if01 可以指向 实现了IF接口类的对象实例IF if01 = new Monster();if01 = new Car();//继承体现的多态//父类类型的变量 a 可以指向 继承AAA的子类的对象实例AAA a = new BBB();a = new CCC();}
}interface IF {}
class Monster implements IF{}
class Car implements IF{}class AAA {}
class BBB extends AAA {}
class CCC extends AAA {}
- 多态数组
案例:给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone特有的方法call
package com.hspedu.interface_;public class InterfacePolyArr {public static void main(String[] args) {//多态数组 -> 接口类型数组Usb[] usbs = new Usb[2];usbs[0] = new Phone_();usbs[1] = new Camera_();/*给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(),请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,还需要调用Phone 特有方法 call*/for(int i = 0; i < usbs.length; i++) {usbs[i].work();//动态绑定..//和前面一样,我们仍然需要进行类型的向下转型if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_((Phone_) usbs[i]).call();}}}
}interface Usb{void work();
}
class Phone_ implements Usb {public void call() {System.out.println("手机可以打电话...");}@Overridepublic void work() {System.out.println("手机工作中...");}
}
class Camera_ implements Usb {@Overridepublic void work() {System.out.println("相机工作中...");}
}
- 接口存在多态传递现象
package com.hspedu.interface_;/*** 演示多态传递现象*/
public class InterfacePolyPass {public static void main(String[] args) {//接口类型的变量可以指向,实现了该接口的类的对象实例IG ig = new Teacher();//如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口//那么,实际上就相当于 Teacher 类也实现了 IH接口.//这就是所谓的 接口多态传递现象.IH ih = new Teacher();}
}interface IH {void hi();
}
interface IG extends IH{ }
class Teacher implements IG {@Overridepublic void hi() {}
}
课堂练习
package com.hspedu.interface_;public class InterfaceExercise02 {public static void main(String[] args) {}
}interface A { // 1min 看看int x = 0;
} //想到 等价 public static final int x = 0;class B {int x = 1;
} //普通属性class C extends B implements A {public void pX() {//System.out.println(x); //错误,原因不明确x//可以明确的指定x//访问接口的 x 就使用 A.x//访问父类的 x 就使用 super.xSystem.out.println(A.x + " " + super.x);}public static void main(String[] args) {new C().pX();}
}
内部类
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class)嵌套其他类的类称为外部类(other class)。是我们类的五大成员(五大成员:属性,方法,构造器,代码块,内部类)内部类最大的特点就是直接访问私有属性,并且可以体现类与类之间的包含关系
基本语法
class Outer{//外部类class Inner{//内部类}
}
class Other{//外部其他类
}
package com.hspedu.innerclass;public class InnerClass01 { //外部其他类public static void main(String[] args) {}
}
class Outer { //外部类private int n1 = 100;//属性public Outer(int n1) {//构造器this.n1 = n1;}public void m1() {//方法System.out.println("m1()");}{//代码块System.out.println("代码块...");}class Inner { //内部类, 在Outer类的内部}
}
内部类的分类
- 定义在外部局部位置上(比如方法内)
局部内部类(有类名)
匿名内部类(没有类名) - 定义在外部类的成员位置上
成员内部类(没有static修饰)
静态内部类(使用static修饰)
局部内部类
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且没有类名
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的低位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以用法final
- 作用域:仅仅在定义它的方法或代码块中
- 局部变量——访问——>外部类的成员(访问方式:直接访问)
- 外部类——访问——>局部内部类发成员
访问方式:创建对象,再访问(注:必须在作用域内)
小结:①局部内部类定义再方法/代码块中
②作用域再方法体或者代码块中
③本质仍然是一个类
- 外部其他类——不能访问——>局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
package com.hspedu.innerclass;
/*** 演示局部内部类的使用*/
public class LocalInnerClass {//public static void main(String[] args) {//演示一遍Outer02 outer02 = new Outer02();outer02.m1();System.out.println("outer02的hashcode=" + outer02);}
}class Outer02 {//外部类private int n1 = 100;private void m2() {System.out.println("Outer02 m2()");}//私有方法public void m1() {//方法//1.局部内部类是定义在外部类的局部位置,通常在方法//3.不能添加访问修饰符,但是可以使用final 修饰//4.作用域 : 仅仅在定义它的方法或代码块中final class Inner02 {//局部内部类(本质仍然是一个类)//2.可以直接访问外部类的所有成员,包含私有的private int n1 = 800;public void f1() {//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,// 使用 外部类名.this.成员)去访问// 老韩解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);System.out.println("Outer02.this hashcode=" + Outer02.this);m2();}}//6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可Inner02 inner02 = new Inner02();inner02.f1();}}
匿名内部类(重点)
注:①本质是类②内部类③该类没有名字④同时是一个实例对象
说明:匿名内部类是定义再外部类的局部位置,比如方法中,并且没有类名
- 匿名内部类的基本语法
new 类或接口(参数){类体
};
package com.hspedu.innerclass;/*** 演示匿名内部类的使用*/
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}
}class Outer04 { //外部类private int n1 = 10;//属性public void method() {//方法//基于接口的匿名内部类//老韩解读//1.需求: 想使用IA接口,并创建对象//2.传统方式,是写一个类,实现该接口,并创建对象//3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用//4. 可以使用匿名内部类来简化开发//5. tiger的编译类型 ? IA//6. tiger的运行类型 ? 就是匿名内部类 Outer04$1/*我们看底层 会分配 类名 Outer04$1class Outer04$1 implements IA {@Overridepublic void cry() {System.out.println("老虎叫唤...");}}*///7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址// 返回给 tiger//8. 匿名内部类使用一次,就不能再使用IA tiger = new IA() {@Overridepublic void cry() {System.out.println("老虎叫唤...");}};System.out.println("tiger的运行类型=" + tiger.getClass());tiger.cry();tiger.cry();tiger.cry();// IA tiger = new Tiger();
// tiger.cry();//演示基于类的匿名内部类//分析//1. father编译类型 Father//2. father运行类型 Outer04$2//3. 底层会创建匿名内部类/*class Outer04$2 extends Father{@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}}*///4. 同时也直接返回了 匿名内部类 Outer04$2的对象//5. 注意("jack") 参数列表会传递给 构造器Father father = new Father("jack"){@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}};System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2father.test();//基于抽象类的匿名内部类Animal animal = new Animal(){@Overridevoid eat() {System.out.println("小狗吃骨头...");}};animal.eat();}
}interface IA {//接口public void cry();
}
//class Tiger implements IA {
//
// @Override
// public void cry() {
// System.out.println("老虎叫唤...");
// }
//}
//class Dog implements IA{
// @Override
// public void cry() {
// System.out.println("小狗汪汪...");
// }
//}class Father {//类public Father(String name) {//构造器System.out.println("接收到name=" + name);}public void test() {//方法}
}abstract class Animal { //抽象类abstract void eat();
}
这里注意底层有继承和实现
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析就可以看出这个特点,因此可以调用匿名内部类方法
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 匿名内部类——访问——>外部类成员(直接访问)
- 外部其他类——不能访问——>匿名内部类(因为他是局部变量)
- 如果外部其他类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类.this.成员)去访问
package com.hspedu.innerclass;public class AnonymousInnerClassDetail {public static void main(String[] args) {Outer05 outer05 = new Outer05();outer05.f1();//外部其他类---不能访问----->匿名内部类System.out.println("main outer05 hashcode=" + outer05);}
}class Outer05 {private int n1 = 99;public void f1() {//创建一个基于类的匿名内部类//不能添加访问修饰符,因为它的地位就是一个局部变量//作用域 : 仅仅在定义它的方法或代码块中Person p = new Person(){private int n1 = 88;@Overridepublic void hi() {//可以直接访问外部类的所有成员,包含私有的//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +" 外部内的n1=" + Outer05.this.n1 );//Outer05.this 就是调用 f1的 对象System.out.println("Outer05.this hashcode=" + Outer05.this);}};p.hi();//动态绑定, 运行类型是 Outer05$1//也可以直接调用, 匿名内部类本身也是返回对象// class 匿名内部类 extends Person {}
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");}
}class Person {//类public void hi() {System.out.println("Person hi()");}public void ok(String str) {System.out.println("Person ok() " + str);}
}
//抽象类/接口...
匿名内部类实践
package com.hspedu.innerclass;public class InnerClassExercise02 {public static void main(String[] args) {/*1.有一个铃声接口Bell,里面有个ring方法。(右图)2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型(右图)3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了4.再传入另一个匿名内部类(对象),打印:小伙伴上课了*/CellPhone cellPhone = new CellPhone();//老韩解读//1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1//2. 重写了 ring//3. Bell bell = new Bell() {// @Override// public void ring() {// System.out.println("懒猪起床了");// }// }cellPhone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("懒猪起床了");}});cellPhone.alarmClock(new Bell() {@Overridepublic void ring() {System.out.println("小伙伴上课了");}});}
}
interface Bell{ //接口void ring();//方法
}
class CellPhone{//类public void alarmClock(Bell bell){//形参是Bell接口类型System.out.println(bell.getClass());bell.ring();//动态绑定}
}
成员内部类
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符(public protected,默认,private),因为它的地位就是一个成员
- 作用域:和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部对象,再调方法
- 成员内部类——访问——>外部类(比如:属性)[访问方式:直接访问]
- 外部类——访问——>内部类【访问方式:创建对象,再访问】
- 外部其他类——访问——>成员内部类(两种方式)
package com.hspedu.innerclass;public class MemberInnerClass01 {public static void main(String[] args) {Outer08 outer08 = new Outer08();outer08.t1();//外部其他类,使用成员内部类的三种方式//老韩解读// 第一种方式// outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员// 这就是一个语法,不要特别的纠结.Outer08.Inner08 inner08 = outer08.new Inner08();inner08.say();// 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象Outer08.Inner08 inner08Instance = outer08.getInner08Instance();inner08Instance.say();}
}class Outer08 { //外部类private int n1 = 10;public String name = "张三";private void hi() {System.out.println("hi()方法...");}//1.注意: 成员内部类,是定义在外部内的成员位置上//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员public class Inner08 {//成员内部类private double sal = 99.8;private int n1 = 66;public void say() {//可以直接访问外部类的所有成员,包含私有的//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.//,可以通过 外部类名.this.属性 来访问外部类的成员System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1);hi();}}//方法,返回一个Inner08实例public Inner08 getInner08Instance(){return new Inner08();}//写方法public void t1() {//使用成员内部类//创建成员内部类的对象,然后使用相关的方法Inner08 inner08 = new Inner08();inner08.say();System.out.println(inner08.sal);}
}
静态内部类
说明:静态内部类是定义在外部类成员位置,并且有static修饰
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public protected,默认,private),因为它的地位就是一个成员
- 作用域:同其他的成员,为整个类体
- 静态内部类——访问——>外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
- 外部类——访问——>静态内部类(访问方式:创建对象,再访问)
- 外部其他类——访问——>静态内部类(三种方式)
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
package com.hspedu.innerclass;public class StaticInnerClass01 {public static void main(String[] args) {Outer10 outer10 = new Outer10();outer10.m1();//外部其他类 使用静态内部类//方式1//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)Outer10.Inner10 inner10 = new Outer10.Inner10();inner10.say();//方式2//编写一个方法,可以返回静态内部类的对象实例.Outer10.Inner10 inner101 = outer10.getInner10();System.out.println("============");inner101.say();Outer10.Inner10 inner10_ = Outer10.getInner10_();System.out.println("************");inner10_.say();}
}class Outer10 { //外部类private int n1 = 10;private static String name = "张三";private static void cry() {}//Inner10就是静态内部类//1. 放在外部类的成员位置//2. 使用static 修饰//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员//5. 作用域 :同其他的成员,为整个类体static class Inner10 {private static String name = "韩顺平教育";public void say() {//如果外部类和静态内部类的成员重名时,静态内部类访问的时,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)System.out.println(name + " 外部类name= " + Outer10.name);cry();}}public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问Inner10 inner10 = new Inner10();inner10.say();}public Inner10 getInner10() {return new Inner10();}public static Inner10 getInner10_() {return new Inner10();}
}
枚举
枚举的引出
package com.hspedu.enum_;public class Enumeration01 {public static void main(String[] args) {//使用Season spring = new Season("春天", "温暖");Season winter = new Season("冬天", "寒冷");Season summer = new Season("夏天", "炎热");Season autumn = new Season("秋天", "凉爽");
// autumn.setName("XXX");
// autumn.setDesc("非常的热..");//因为对于季节而已,他的对象(具体值),是固定的四个,不会有更多//安老师的这个设计类的思路,不能体现季节是固定的四个对象//因此,这样的设计不好===> 枚举类[枚: 一个一个 举: 例举 , //即把具体的对象一个一个例举出来的类// 就称为枚举类]Season other = new Season("红天", "~~~");}
}
class Season{//类private String name;private String desc;//描述public Season(String name, String desc) {this.name = name;this.desc = desc;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
}
枚举的两种实现方法
- 自定义类实现枚举
- 使用enum关键字实现枚举
自定义类实现枚举
- 不需要提供setXxx方法,因为枚举对象值通常为只读
- 对枚举对象/属性使用final + static 共同修饰,实现底层优化
- 枚举对象名通常使用全部大写,常量的命名规范
- 枚举对象根据需要,也可以有多个属性
package com.hspedu.enum_;public class Enumeration02 {public static void main(String[] args) {System.out.println(Season.AUTUMN);System.out.println(Season.SPRING);}
}//演示字定义枚举实现
class Season {//类private String name;private String desc;//描述//定义了四个对象, 固定.public static final Season SPRING = new Season("春天", "温暖");public static final Season WINTER = new Season("冬天", "寒冷");public static final Season AUTUMN = new Season("秋天", "凉爽");public static final Season SUMMER = new Season("夏天", "炎热");//1. 将构造器私有化,目的防止 直接 new//2. 去掉setXxx方法, 防止属性被修改//3. 在Season 内部,直接创建固定的对象//4. 优化,可以加入 final 修饰符private Season(String name, String desc) {this.name = name;this.desc = desc;}public String getName() {return name;}public String getDesc() {return desc;}@Overridepublic String toString() {return "Season{" +"name='" + name + '\'' +", desc='" + desc + '\'' +'}';}
}
enum关键字实现枚举
- 当我们使用enum关键字开发一个枚举类时,默认会继承Enum类,而且时一个final类(用javap证明)
- 传统的public static final Season SPRING = new Season(“春天”, “温暖”); 简化成SPRING(“春天”,“温暖”),这里必须知道,它调用的是哪个构造器
- 如果使用无参构造器创建枚举对象,则实参表和小括号可以省略
- 当有多个枚举对象时,使用,间隔,最后有一个分号结尾
- 枚举对象必须放在枚举类的首行
package com.hspedu.enum_;/*** @author 韩顺平* @version 1.0*/
public class Enumeration03 {public static void main(String[] args) {System.out.println(Season2.AUTUMN);System.out.println(Season2.SUMMER);}
}
//演示使用enum关键字来实现枚举类
enum Season2 {//类//定义了四个对象, 固定.
// public static final Season SPRING = new Season("春天", "温暖");
// public static final Season WINTER = new Season("冬天", "寒冷");
// public static final Season AUTUMN = new Season("秋天", "凉爽");
// public static final Season SUMMER = new Season("夏天", "炎热");//如果使用了enum 来实现枚举类//1. 使用关键字 enum 替代 class//2. public static final Season SPRING = new Season("春天", "温暖") 直接使用// SPRING("春天", "温暖") 解读 常量名(实参列表)//3. 如果有多个常量(对象), 使用 ,号间隔即可//4. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面//5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"),SUMMER("夏天", "炎热")/*, What()*/;private String name;private String desc;//描述private Season2() {//无参构造器}private Season2(String name, String desc) {this.name = name;this.desc = desc;}public String getName() {return name;}public String getDesc() {return desc;}@Overridepublic String toString() {return "Season{" +"name='" + name + '\'' +", desc='" + desc + '\'' +'}';}
}
enum练习
enum成员方法
说明:使用关键字enum时,会隐式继承Enum类,这样我们就可以使用Enum类相关的方法
- toString : Enum类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息
- name : 返回当前对象名(常量名),子类中不能重写
- ordinal : 返回当前对象的位置号,默认从0开始
- values: 返回当前枚举类中所有的常量
- valuesOf : 将字符串转成枚举对象,要求字符串必须为已有的常量名,否则报异常
- compareTo : 比较两个枚举常量,比较的就是位置号
package com.hspedu.enum_;/*** @author 韩顺平* @version 1.0* 演示Enum类的各种方法的使用*/
public class EnumMethod {public static void main(String[] args) {//使用Season2 枚举类,来演示各种方法Season2 autumn = Season2.AUTUMN;//输出枚举对象的名字System.out.println(autumn.name());//ordinal() 输出的是该枚举对象的次序/编号,从0开始编号//AUTUMN 枚举对象是第三个,因此输出 2System.out.println(autumn.ordinal());//从反编译可以看出 values方法,返回 Season2[]//含有定义的所有枚举对象Season2[] values = Season2.values();System.out.println("===遍历取出枚举对象(增强for)====");for (Season2 season: values) {//增强for循环System.out.println(season);}//valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常//执行流程//1. 根据你输入的 "AUTUMN" 到 Season2的枚举对象去查找//2. 如果找到了,就返回,如果没有找到,就报错Season2 autumn1 = Season2.valueOf("AUTUMN");System.out.println("autumn1=" + autumn1);System.out.println(autumn == autumn1);//compareTo:比较两个枚举常量,比较的就是编号//老韩解读//1. 就是把 Season2.AUTUMN 枚举对象的编号 和 Season2.SUMMER枚举对象的编号比较//2. 看看结果/*public final int compareTo(E o) {return self.ordinal - other.ordinal;}Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3]*/System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));//补充了一个增强for
// int[] nums = {1, 2, 9};
// //普通的for循环
// System.out.println("=====普通的for=====");
// for (int i = 0; i < nums.length; i++) {
// System.out.println(nums[i]);
// }
// System.out.println("=====增强的for=====");
// //执行流程是 依次从nums数组中取出数据,赋给i, 如果取出完毕,则退出for
// for(int i : nums) {
// System.out.println("i=" + i);
// }}
}
enum课堂练习
package com.hspedu.enum_;/*** @author 韩顺平* @version 1.0*/
public class EnumExercise02 {public static void main(String[] args) {//获取到所有的枚举对象, 即数组Week[] weeks = Week.values();//遍历,使用增强forSystem.out.println("===所有星期的信息如下===");for (Week week : weeks) {System.out.println(week);}}
}/*
声明Week枚举类,其中包含星期一至星期日的定义;
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
使用values 返回所有的枚举数组, 并遍历 , 输出左图效果*/enum Week {//定义Week的枚举对象MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");private String name;private Week(String name) {//构造器this.name = name;}@Overridepublic String toString() {return name;}
}
enum实现接口
- 使用enum关键字后,就不能再继续继承其他类了,因为enum会隐式继承Enum,而java是单继承机制
- 枚举类和普通类一样,可以实现接口,如下形式。
enum 类名 implements 接口1,接口2{}
package com.hspedu.enum_;/*** @author 韩顺平* @version 1.0*/
public class EnumDetail {public static void main(String[] args) {Music.CLASSICMUSIC.playing();}
}
class A {}//1.使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
//enum Season3 extends A {
//
//}
//2.enum实现的枚举类,仍然是一个类,所以还是可以实现接口的.
interface IPlaying {public void playing();
}
enum Music implements IPlaying {CLASSICMUSIC;@Overridepublic void playing() {System.out.println("播放好听的音乐...");}
}
注解
注解的理解
- 注解(Annotation)也被称为元数据(Metadate)用于修饰解释包,类,方法,属性,构造器,局部变量等数据信息
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入再代码中的补充信息
- 再javaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。再javaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中所遗留的繁冗代码和XML配置等
基本的Annotation案例
@Override 注解
- @Override:限定某个方法,是重写父类方法,该注解中能用于方法
补充:@interface 不是interface,是注解类,是jdk5.0以后加入的
package com.hspedu.annotation_;/*** @author 韩顺平* @version 1.0*/
public class Override_ {public static void main(String[] args) {}
}
class Father{//父类public void fly(){int i = 0;System.out.println("Father fly...");}public void say(){}}class Son extends Father {//子类//老韩解读//1. @Override 注解放在fly方法上,表示子类的fly方法时重写了父类的fly//2. 这里如果没有写 @Override 还是重写了父类fly//3. 如果你写了@Override注解,编译器就会去检查该方法是否真的重写了父类的// 方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误//4. 看看 @Override的定义// 解读: 如果发现 @interface 表示一个 注解类/*@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}*/@Override //说明public void fly() {System.out.println("Son fly....");}@Overridepublic void say() {}
}
- @Override 使用说明
- @Override表示指定重写父类方法(从编译层面验证)如果父类没有fly方法,则会报错
- 如果不写@Override注解,而父类仍有public void fly(){},仍然构成重写
- @Override只能修饰方法,不能修饰其他类,包,属性等
- 查看@Override注解源码为@Target(ElementType METHOD),说明只能修饰方法
- @Target是修饰注解的注解,称为元注解
@Ddeprecated 注解
- @Ddeprecated :用于表示某个程序元素(类,方法)已过时
- 可以修饰方法,类,字段,包,参数等等
- @Target(value = {CONSTRUCTOR,FLELD,LOCK_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE})
- @Ddeprecated 的作用可以做到新旧版本的兼容和过度
package com.hspedu.annotation_;/*** @author 韩顺平* @version 1.0*/
public class Deprecated_ {public static void main(String[] args) {A a = new A();a.hi();System.out.println(a.n1);}
}//老韩解读
//1. @Deprecated 修饰某个元素, 表示该元素已经过时
//2. 即不在推荐使用,但是仍然可以使用
//3. 查看 @Deprecated 注解类的源码
//4. 可以修饰方法,类,字段, 包, 参数 等等
//5. @Deprecated 可以做版本升级过渡使用
/*
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}*/
@Deprecated
class A {@Deprecatedpublic int n1 = 10;@Deprecatedpublic void hi(){}
}
@SuppressWarnings 注解
@SuppressWarnings:抑制编译器警告
- unchecked是忽略没有检查的警告
- rawtypes是忽略没有指定泛型的警告(传参是没有指定泛型的错误警告)
- unused是忽略没有使用某个变量的警告错误
- @SuppressWarnings,可以修饰的程序元素,查看@Target
- 生成@SuppressWarnings时,不用背直接点击左侧的黄色提示,就可以选择
package com.hspedu.annotation_;import java.util.ArrayList;
import java.util.List;/*** @author 韩顺平* @version 1.0*/
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
public class SuppressWarnings_ {//老韩解读//1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息//2. 在{""} 中,可以写入你希望抑制(不显示)警告信息//3. 可以指定的警告类型有// all,抑制所有警告// boxing,抑制与封装/拆装作业相关的警告// //cast,抑制与强制转型作业相关的警告// //dep-ann,抑制与淘汰注释相关的警告// //deprecation,抑制与淘汰的相关警告// //fallthrough,抑制与switch陈述式中遗漏break相关的警告// //finally,抑制与未传回finally区块相关的警告// //hiding,抑制与隐藏变数的区域变数相关的警告// //incomplete-switch,抑制与switch陈述式(enum case)中遗漏项目相关的警告// //javadoc,抑制与javadoc相关的警告// //nls,抑制与非nls字串文字相关的警告// //null,抑制与空值分析相关的警告// //rawtypes,抑制与使用raw类型相关的警告// //resource,抑制与使用Closeable类型的资源相关的警告// //restriction,抑制与使用不建议或禁止参照相关的警告// //serial,抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告// //static-access,抑制与静态存取不正确相关的警告// //static-method,抑制与可能宣告为static的方法相关的警告// //super,抑制与置换方法相关但不含super呼叫的警告// //synthetic-access,抑制与内部类别的存取未最佳化相关的警告// //sync-override,抑制因为置换同步方法而遗漏同步化的警告// //unchecked,抑制与未检查的作业相关的警告// //unqualified-field-access,抑制与栏位存取不合格相关的警告// //unused,抑制与未用的程式码及停用的程式码相关的警告//4. 关于SuppressWarnings 作用范围是和你放置的位置相关// 比如 @SuppressWarnings放置在 main方法,那么抑制警告的范围就是 main// 通常我们可以放置具体的语句, 方法, 类.//5. 看看 @SuppressWarnings 源码//(1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE//(2) 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}/*@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {String[] value();}*/public static void main(String[] args) {List list = new ArrayList();list.add("jack");list.add("tom");list.add("mary");int i;System.out.println(list.get(1));}public void f1() {
// @SuppressWarnings({"rawtypes"})List list = new ArrayList();list.add("jack");list.add("tom");list.add("mary");
// @SuppressWarnings({"unused"})int i;System.out.println(list.get(1));}
}
元注解
基本介绍
JDK的元Annotation用于修饰其他Annotation
元注解的种类
- Retention //指定注解的作用范围,三种:SOURCE(源文件编译),CLASS(类),RUNTIME(运行)
- Target //指定注解可以在那些地方使用
- Documented //指定该注解是否会在javadoc体现
- Inherited //子类会继承父类注解
@Retention 注解
注:Override的作用域在SOURCE,当编译器编译时生效,不会写入到.class文件,也不会在runtime(运行)时生效
@Target 注解
- 基本说明:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰那些程序元素@Target也包含一个名为value的成员变量
@Documented 注解
- 基本说明:@Documented:用于指定被该元注解修饰的注解类将被javadoc工具提取生成文档时,可以看到该注解
- 注:定义为Documented的注解必须设置Retention的值为RUNTIME
@Inherited 注解
被它修饰的Annotation将具有继承性。如果某个父类使用了被@Inherited修饰的注解,则其子类将自动拥有该注解
章节作业
package com.hspedu.homework;/*** @author 韩顺平* @version 1.0*/
public class Homework04 {public static void main(String[] args) {Cellphone cellphone = new Cellphone();//老韩解读//1. 匿名内部类是/*new ICalculate() {@Overridepublic double work(double n1, double n2) {return n1 + n2;}}, 同时也是一个对象他的编译类型 ICalculate, 他的运行类型就是 匿名内部类*/cellphone.testWork(new ICalculate() {@Overridepublic double work(double n1, double n2) {return n1 + n2;}}, 10, 8);//18.0cellphone.testWork(new ICalculate() {@Overridepublic double work(double n1, double n2) {return n1 * n2;}}, 10, 8);}
}
/*
1.计算器接口具有work方法,功能是运算,有一个手机类Cellphone,定义方法testWork测试计算功能,调用计算接口的work方法,
2.要求调用CellPhone对象 的testWork方法,使用上 匿名内部类*/
//编写接口
interface ICalculate {//work方法 是完成计算,但是题没有具体要求,所以自己设计//至于该方法完成怎样的计算,我们交给匿名内部类完成public double work(double n1, double n2) ;
}
class Cellphone {//老韩解读,当我们调用testWork方法时,直接传入一个实现了ICalculate接口的匿名内部类即可//该匿名内部类,可以灵活的实现work,完成不同的计算任务public void testWork(ICalculate iCalculate, double n1, double n2) {double result = iCalculate.work(n1, n2);//动态绑定System.out.println("计算后的结果是=" + result);}
}
注:这种匿名内部类情况属于work的计算方法,可以在调用时改变
package com.hspedu.homework;/*** @author 韩顺平* @version 1.0*/
public class Homework06 {public static void main(String[] args) {Person tang = new Person("唐僧", new Horse());tang.common();//一般情况下tang.passRiver();//过河tang.common();//一般情况下tang.passRiver();//过河tang.passRiver();//过河tang.passRiver();//过河//过火焰山tang.passFireHill();}
}
/*
1.有一个交通工具接口类Vehicles,有work接口
2.有Horse类和Boat类分别实现Vehicles
3.创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat
4.有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
5.实例化Person对象“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具
6.增加一个情况,如果唐僧过火焰山, 使用 飞机 ==> 程序扩展性, 我们前面的程序结构就非常好扩展 10min
使用代码实现上面的要求
编程 需求---->理解---->代码-->优化*/
interface Vehicles {//有一个交通工具接口类Vehicles,有work接口public void work();
}class Boat implements Vehicles {@Overridepublic void work() {System.out.println(" 过河的时候,使用小船.. ");}
}class Horse implements Vehicles {@Overridepublic void work() {System.out.println(" 一般情况下,使用马儿前进...");}
}class Plane implements Vehicles {@Overridepublic void work() {System.out.println("过火焰山,使用飞机...");}
}class VehiclesFactory {//马儿始终是同一匹private static Horse horse = new Horse(); //饿汉式private VehiclesFactory(){}//创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat//这里,我们将方法做成staticpublic static Horse getHorse() {
// return new Horse();return horse;}public static Boat getBoat() {return new Boat();}public static Plane getPlane() {return new Plane();}
}class Person {private String name;private Vehicles vehicles;//在创建人对象时,事先给他分配一个交通工具public Person(String name, Vehicles vehicles) {this.name = name;this.vehicles = vehicles;}//实例化Person对象“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具//这里涉及到一个编程思路,就是可以把具体的要求,封装成方法-> 这里就是编程思想//思考一个问题,如何不浪费,在构建对象时,传入的交通工具对象->动脑筋public void passRiver() {//先得到船//判断一下,当前的 vehicles 属性是null, 就获取一艘船
// Boat boat = VehiclesFactory.getBoat();
// boat.work();//如何防止始终使用的是传入的马 instanceOf//if (vehicles == null) {//vehicles instanceof Boat 是判断 当前的 vehicles是不是Boat//(1) vehicles = null : vehicles instanceof Boat => false//(2) vehicles = 马对象 :vehicles instanceof Boat => false//(3) vehicles = 船对象 :vehicles instanceof Boat => trueif (!(vehicles instanceof Boat)) {vehicles = VehiclesFactory.getBoat();}vehicles.work();}public void common() {//得到马儿//判断一下,当前的 vehicles 属性是null, 就获取一匹马//if (vehicles == null) {if (!(vehicles instanceof Horse)) {//这里使用的是多态vehicles = VehiclesFactory.getHorse();}//这里体现使用接口调用vehicles.work();}//过火焰山public void passFireHill() {if (!(vehicles instanceof Plane)) {//这里使用的是多态vehicles = VehiclesFactory.getPlane();}//这里体现使用接口调用vehicles.work();}
}//有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
注:这里通过一个类来获取不同的对象实例,在函数中调用该方法获得具体实现接口的实例,并赋给了接口类型引用(vehicles)在主函数调用时来实现动态绑定
并且这里有单例设计(饿汉式):由于马匹用的时同一对象,故用单例设计来实现这一功能,而船和飞机则调用一次创建一个新对象
package com.hspedu.homework;/*** @author 韩顺平* @version 1.0*/
public class Homework07 {public static void main(String[] args) {//实例化不同的car对象Car2 car2 = new Car2(60);car2.getAir().flow();Car2 car21 = new Car2(-1);car21.getAir().flow();Car2 car22 = new Car2(20);car22.getAir().flow();}
}
/*
有一个Car2类,有属性temperature(温度),车内有Air(空调)类,有吹风的功能flow,
Air会监视车内的温度,如果温度超过40度则吹冷气。如果温度低于0度则吹暖气,
如果在这之间则关掉空调。实例化具有不同温度的Car对象,调用空调的flow方法,
测试空调吹的风是否正确 . //体现 类与类的包含关系的案例 类(内部类【成员内部类】)*/
class Car2 {private double temperature;public Car2(double temperature) {this.temperature = temperature;}//Air 成员内部类class Air {public void flow() {if(temperature > 40) {System.out.println("温度大于40 空调吹冷气..");} else if(temperature < 0) {System.out.println("温度小于0 空调吹暖气..");} else {System.out.println("温度正常,关闭空调..");}}}//返回一个Air对象public Air getAir() {return new Air();}
}
注:这里体现了类与内部类之间的联系:内部类通过外部类的属性(温度)来决定功能
package com.hspedu.homework;/*** @author 韩顺平* @version 1.0*/
public class Homework08 {public static void main(String[] args) {//演示一下枚举值得switch使用Color green = Color.GREEN;green.show();//比较一下//switch () 中,放入枚举对象//在每个case 后,直接写上在枚举类中,定义的枚举对象即可switch (green) {case YELLOW:System.out.println("匹配到黄色");break;case BLACK:System.out.println("匹配到黑色");break;default:System.out.println("没有匹配到..");}}
}/*
枚举类
创建一个Color枚举类
1.有 RED,BLUE,BLACK,YELLOW,GREEN这个五个枚举值/对象;
2.Color有三个属性redValue,greenValue,blueValue,
3.创建构造方法,参数包括这三个属性,
4.每个枚举值都要给这三个属性赋值,三个属性对应的值分别是
red:255,0,0 blue:0,0,255 black:0,0,0 yellow:255,255,0 green:0,255,0
5.定义接口,里面有方法show,要求Color实现该接口
6.show方法中显示三属性的值
7. 将枚举对象在switch语句中匹配使用*/
interface IMyInterface {public void show();
}enum Color implements IMyInterface {RED(255, 0, 0), BLUE(0, 0, 255), BLACK(0, 0, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0);private int redValue;private int greenValue;private int blueValue;Color(int redValue, int greenValue, int blueValue) {this.redValue = redValue;this.greenValue = greenValue;this.blueValue = blueValue;}@Overridepublic void show() {System.out.println("属性值为" + redValue + "," + greenValue + "," + blueValue);}
}
注:这里注意以枚举对象为返回类型的switch-case的使用
最后
附上韩顺平老师这节内容的章节总结原视频有助于知识梳理
进度443/910 ,学习永无止境!!!