1、内部类
匿名内部类
简单介绍
- 类只是使用一次,后面再不使用,可以简化开发
- (1)本质是(实现接口/继承父类的)类(2)内部类(3)该类没有名字(4)同时还是一个对象
- 匿名内部类的基本语法: new类或接口(参数列表){类体}
- 可以在方法体中还可以在参数中
注意事项
- 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,
- 同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类---访问---->外部类成员[访问方式:直接访问]
- 外部其他类---不能访问----->匿名内部类(因为匿名内部类地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
- 使用时遵循动态绑定
package zhh.chapter09.innerclass;
import java.awt.print.Printable;
public class AnonymousInnerClass {
public static void main(String[] args) {
new outer02().method();
// 输出是:
// 基于接口的匿名内部类 对象调用print1:1
// ia的运行类型: class zhh.chapter09.innerclass.outer02$1
// 基于接口的匿名内部类 直接调用print1:11
// 访问外部类私有成员n1: 10
// 内部n2: 100外部n2: 10
// 基于接口的匿名内部类 对象调用print1:1
//
// 基于类的匿名内部类 对象调用print2:2
// ib的运行类型: class zhh.chapter09.innerclass.outer02$3
// 基于类的匿名内部类 直接调用print2:22
//
// 基于抽象类的匿名内部类 对象调用print3:3
// ic的运行类型: class zhh.chapter09.innerclass.outer02$5
// 基于抽象类的匿名内部类 对象调用print3:33
}
}
class outer02{
private int n1=10;
private int n2=10;
public void method(){
//基于接口的匿名内部类
//5. ia 的编译类型 ? IA
//6. ia 的运行类型 ? 就是匿名内部类 Outer02$1
/*
底层会分配 类名 Outer02$1,类似于
class Outer02$1 implements IA {
@Override
public void print1() {
System.out.println("基于接口的匿名内部类 对象调用print1:"+"1");
}
}
*/
//7. jdk 底层在创建匿名内部类 Outer02$1,立即马上就创建了 Outer02$1 实例,并且把地址
// 返回给 ia
//下面ib ic同理
IA ia = new IA(){
private int n2=100;
@Override
public void print1() {
System.out.println("基于接口的匿名内部类 对象调用print1:"+"1");
}
};
ia.print1();
System.out.println("ia的运行类型: "+ia.getClass());
new IA(){
@Override
public void print1() {
System.out.println("基于接口的匿名内部类 直接调用print1:"+"11");
//内部类直接访问外部成员
System.out.println("访问外部类私有成员n1: "+n1);
//成员重名,就近原则
System.out.println("内部n2: "+n2+"外部n2: "+outer02.this.n2);
}
}.print1();
ia.print1();
//基于类的匿名内部类
IB ib = new IB(){
@Override
public void print2() {
System.out.println("\n基于类的匿名内部类 对象调用print2:"+"2");
}
};
ib.print2();
System.out.println("ib的运行类型: "+ib.getClass());
new IB(){
@Override
public void print2() {
System.out.println("基于类的匿名内部类 直接调用print2:"+"22");
}
}.print2();
//基于抽象类的匿名内部类
IC ic = new IC(){
@Override
void print3() {
System.out.println("\n基于抽象类的匿名内部类 对象调用print3:"+"3");
}
};
ic.print3();
System.out.println("ic的运行类型: "+ic.getClass());
new IC(){
@Override
void print3() {
System.out.println("基于抽象类的匿名内部类 对象调用print3:"+"33");
}
}.print3();
}
}
interface IA{
public void print1();
}
class IB{
public void print2(){
System.out.println("print2:"+"2");
}
}
abstract class IC{
abstract void print3();
}
匿名内部类最佳实践
package zhh.chapter09.innerclass;
public class AnonymousInnerClassBestExer {
public static void main(String[] args) {
f(new IE(){
@Override
public void print4() {
System.out.println("内部类当做实参,直接传递!");
}
});
//当“System.out.println("内部类当做实参,直接传递!");”
//使用多次时,可以用传统方法
//创建类实现接口,创建此类对象作为实参
f(new IF());
// print
// 内部类当做实参,直接传递!
// 创建对象当做实参,直接传递!
}
//参数为类也ok!
//到时传递一个内部类继承此类就行
public static void f(IE ie){
ie.print4();
}
}
interface IE{
public void print4();
}
class IF implements IE{
@Override
public void print4() {
System.out.println("创建对象当做实参,直接传递!");
}
}
成员内部类
没有static修饰
注意事项
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域和外部类的其他成员一样,为整个类体
- 成员内部类---访问---->外部类成员(比如:属性) 【访问方式:直接访问】
- 外部类---访问------>成员内部类 【访问方式:创建对象,再访问】
- 外部其他类---访问---->成员内部类(如代码)
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package zhh.chapter09.innerclass;
public class MemberInnerClass {
public static void main(String[] args) {
Outer3 outer3 = new Outer3();
outer3.t1();
//外部其他类,使用成员内部类的2种方式
// 第一种方式
// outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
// 通过外部类对象去new
Outer3.Inner3 inner08 = outer3.new Inner3();
inner08.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象
Outer3.Inner3 Inner3Instance = outer3.getInner3Instance();
Inner3Instance.say();
// print
// n1 = 66 name = 张三 外部类的 n1=10
// hi()方法...
// 99.8
// n1 = 66 name = 张三 外部类的 n1=10
// hi()方法...
// n1 = 66 name = 张三 外部类的 n1=10
// hi()方法...
}
}
class Outer3 { //外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
public class Inner3 {//成员内部类
private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
// ,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer3.this.n1);
hi();
}
}
//方法,返回一个 Inner08 实例
public Inner3 getInner3Instance(){
return new Inner3();
}
//写方法
public void t1() {
//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner3 inner3 = new Inner3();
inner3.say();
System.out.println(inner3.sal);
}
}
静态内部类
静态内部类是定义在外部类的成员位置,并且有static修饰
注意事项
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域:同其他的成员,为整个类体
- 静态内部类---访问---->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
- 外部类---访问------>静态内部类访问方式:创建对象,再访问
- 外部其他类---访问----->静态内部类(代码)
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
package zhh.chapter09.innerclass;
public class StaticInnerClass {
public static void main(String[] args) {
Outer4 outer4 = new Outer4();
outer4.m1();
//外部其他类 使用静态内部类
//方式 1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer4.Inner4 inner10 = new Outer4.Inner4();
inner10.say();
//方式 2
//编写一个方法,可以返回静态内部类的对象实例.
//普通方法用外部类对象名调用
Outer4.Inner4 inner101 = outer4.getInner4();
System.out.println("============");
inner101.say();
//静态方法用外部类名调用
Outer4.Inner4 inner10_ = Outer4.getInner4_();
System.out.println("************");
inner10_.say();
// print
// 李四 外部类 name= 张三
// 李四 外部类 name= 张三
// ============
// 李四 外部类 name= 张三
// ************
// 李四 外部类 name= 张三
}
}
class Outer4 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner4 就是静态内部类
//1. 放在外部类的成员位置
//2. 使用 static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner4 {
private static String name = "李四";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类 name= " + Outer4.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner4 inner4 = new Inner4();
inner4.say();
}
public Inner4 getInner4() {
return new Inner4();
}
public static Inner4 getInner4_() {
return new Inner4();
}
}
2、枚举
枚举对应英文(enumeration, 简写 enum),一组常量的集合,属于一种特殊的类,里面只包含一组有限的特定的对象。
二种实现方式
- 自定义类实现枚举
- 使用 enum 关键字实现枚举
注意事项
- 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类,所以不能再继承其它类,Java 是单继承机制。但可以实现接口。
- 传统的 public static final Season2 SPRING = new Season2("春天", "温暖"); 简化成 SPRING("春天", "温暖"), 这里必 须知道,它调用的是哪个构造器.
- 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用,间隔,最后有一个分号结尾
- 枚举对象必须放在枚举类的行首
enum常用方法
1) toString:Enum 类已经重写过了,返回的是当前对象 名,子类可以重写该方法,用于返回对象的属性信息
2) name:返回当前对象名(常量名),子类中不能重写
3) ordinal:返回当前对象的位置号,默认从 0 开始
4) values:返回当前枚举类中所有的常量
5) valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常!
6) compareTo:比较两个枚举常量,比较的就是编号!
package zhh.chapter10;
import javax.crypto.SealedObject;
/**
* @author longbownice
* @version 1.0
*
* enum实现枚举
*/
public class Enumeration02 {
public static void main(String[] args) {
//返回当前枚举类中所有的常量
Season2[] season = Season2.values();
for(Season2 s:season){
System.out.println(s);
}
Season2 summer = Season2.SUMMER;
//name:返回当前对象名(常量名),子类中不能重写
System.out.println(summer.name());
//ordinal:返回当前对象的位置号,默认从 0 开始
System.out.println(summer.ordinal());
//valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常!
//感觉没啥用,以后再看
Season2 summer1=Season2.valueOf("SUMMER");
//compareTo:比较两个枚举常量,比较的就是编号!
//return 前面编号 - 后边编号
System.out.println(summer1.compareTo(Season2.WINNER));
}
// print
// Season2{name='春天'}
// Season2{name='夏天'}
// Season2{name='秋天'}
// Season2{name='冬天'}
// Season2{name='null'}
// SUMMER
// 1
// -2
}
//1. 使用关键字 enum 替代 class
enum Season2{
//2. public static final Season SPRING = new Season("春天", "温暖") 直接使用
// SPRING("春天", "温暖") 解读 常量名(实参列表)
//3. 如果有多个常量(对象), 使用 ,号间隔即可
//4. 如果使用 enum 来实现枚举,要求将定义常量对象,写在行首
//5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
SPRING("春天"),SUMMER("夏天"),
AUTUMN("秋天"),WINNER("冬天"),
WHAT;
private String name;
private Season2(){}
private Season2(String name) {
this.name = name;
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
'}';
}
}
3、注解的理解
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角 色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等。
- 使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素
三个基本的 Annotation:
- @Override: 限定某个方法,是重写父类方法, 该注解只能用于方法
- @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
- @SuppressWarnings: 抑制编译器警告
//1. 如果写了@Override 注解,编译器就会去检查该方法是否真的重写了父类的
// 方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
//2. 看看 @Override 的定义
// 解读: 如果发现 @interface 表示一个 注解类
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
*/
@Override
//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
//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();
}
*/
@SuppressWarnings
4、元注解了解
JDK 的元 Annotation 用于修饰其他 Annotation
修饰注解的注解
1) Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
2) Target // 指定注解可以在哪些地方使用
3) Documented //指定该注解是否会在 javadoc 体现
4) Inherited //子类会继承父类注解,被它修饰的Annotation将具有继承性.即如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解
@Retention 注解
说明 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 使用 @Rentention 时必须为该 value 成员变量指定值: @Retention 的三种值
- RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释(javac前)
- RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认值 (javac后)
- RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以 通过反射获取该注解(java后)
/*
//表明只对方法有用
@Target(ElementType.METHOD)
//说明: Override的作用域在SOURCE,当编译器编译时生效,不会写入到.class文件,也不会再runtime(运行时)生效
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
*/
@Override
/*
//表明可以留在javadoc形成的文档中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
*/
@Deprecated