文章目录
内部类
1. 概念:
当一个类的定义,出现在另外一个类中时。那么这个类就是内部类。包含了 内部类
的类,叫做 外部类
。
2. 使用场景:
当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类,这样可以隐藏该类的实现细节,还可以直接访问外部类的私有成员而不再需要提供公有的get和set方法
3. 分类:
-
普通内部类: 直接将一个类的定义放在另外一个类的类体中。
-
静态内部类:使用static关键字修饰的内部类,隶属于类层级。
-
局部内部类: 直接将一个类的定义放在方法体的内部时。
-
匿名内部类: 就是指没有名字的内部类。
4. 定义和使用:
-
普通内部类:
普通内部类,属于外部类中的一个属性,并且是对象层级
// Outer类是外部类 public class Outer{ private int cnt = 1; // 定义普通内部类:Inner public class Inner{ private int innerNum = 10; private int cnt = 3;// 和外部类中的属性同名 // 内部类可以直接调用外部类的私有方法 innerNum += cnt; // System.out.println("内部类中的 cnt = "+ this.cnt); System.out.println("外部类中的 cnt = "+ Outer.this.cnt); } } /* 内部类的调用 */ // 因为普通内部类,属于对象层级,需要外部类的引用,才可以访问 Outer outer = new Outer(); Outer.Inner in = outer.new inner(); // 调用内部类的方法和属性,是需要先创建内部类中的对象的 System.out.println(in.innerNum);
-
静态内部类:
静态,static,指提升到类层级,直接用类名. 就可访问。
注:要访问普通成员变量和方法,要通过引用来访问。而静态类,随着类的加载而加载,是先于引用和对象的创建。所以,static修饰的成员,不能访问非静态的成员变量或方法
public class Outer{ private int cnt = 1; private staitc int snt = 2; // 定义静态内部类 public static class StaticInner{ // 不能访问外部类的非静态方法 private innerNum = snt; private snt = 10; // private innerNum = cnt; // 不能访问cnt System.out.println("内部类中的 snt = "+ StaticInner.snt); System.out.println("外部类中的 snt = "+ Outer.snt); } } /* 静态内部类的调用 */ Outer.StaticInner os = new Outer.StaticInner(); System.out.println(os.innerNum);
-
局部内部类:
- 局部内部类,定义在方法内部
- 局部内部类中,不能使用访问控制符和static修饰符。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的
public class Outer{ private int cnt = 1; public static final snt = 12; public void show(){ // 定依局部内部类 class Inner{ int num = snt; ... } } }
-
匿名内部类(重点):
-
语法格式
接口 引用名 = new 接口(){ 方法的重写; }; --------------------------- // 举例 public interface A{ void show(); } public class b{ // 使用匿名内部类 A a = new A(){ @Override public void show(){ System.out.println("这是接口的实现类!不过是匿名"); } }; }
-
为什么叫做匿名内部类?
接口是不能直接去new(实例化)的,要实例化就要有实现类。所以,用接口类型的引用指向 匿名的实现类对象。运用了多态,这样就能正常使用该接口。
-
从 Java8 ,推出了新特性:lamda表达式,来简化匿名内部类的代码
-
格式:
(参数列表) -> 方法体;
-
根据上面的例子,简化匿名内部类的代码
// 重写的show方法,没有形参,所以()内也不写 A a =()-> System.out.println("lamda 如此简单");
-
-
总结一下,类中有哪些东西:
-
成员变量(属性),成员方法,构造方法,静态成员 (static修饰的属性、方法),静态代码块,代码块,内部类
-
为什么要使用匿名内部类?
因为,当类或接口作为参数时,并且只用一次,那么为其单独写一个实现类,得不偿失。它会加载进内存一直占用内存 (方法区),直到程序结束,才会被释放,所以消耗内存空间
枚举
1. 基本概念:
-
生活中,常见的枚举:
- 性别:男、女
- 每周的:周一到周日
- 一年的:春、夏、秋、冬
- LOL 默认键位: Q、W、E、R
- 开发中:描述一些状态信息,比如错误码等。
-
什么是枚举?
生活中某些事物是有序且固定的。我们把这些事物一一列出,列出的类型就叫枚举类型。
-
注意事项:
-
枚举,Java5开始增加的一种引用数据类型,用enum关键字来定
义 -
枚举类中存储的值,默认使用 public static final 关键字共同修饰,所以是类级别的,直接用 枚举类型. 的方式调用
-
枚举中常量的类型,就是 枚举类类型
-
枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的。
-
2. 枚举的用法:
1)常量
Java5之前,常量是用 public static final 表示,常量一多,描述就较为繁琐。Java5之后,使用 enum关键字来定义枚举类型取代常量
// 定义一个枚举类Season,用来描述季节
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
2)switch
public Class enum SeasonTest{
// switch结构
public void test(Season se) {
switch (se) {
case SPRING:
System.out.println("春天到了");
break;
case SUMMER:
System.out.println("夏天到了");
break;
case AUTUMN:
System.out.println("秋天到了");
break;
case WINTER:
System.out.println("冬天到了");
break;
}
}
}
// 调用
SeasonTest.test(Season.AUTUMN);
------------------------ " 结果 "
秋天到了
3)实现接口
枚举默认继承了 java.lang.Enum
类 , 所以枚举类不能用于继承 (单继承)
// 接口
interface GetSeason {
// 得到季节信息
String getInfo();
}
// GetSeason 接口的实现类
enum Season implements GetSeason {
private String name;
private String time;
// 对象
SPRING("春","3-5"),
SUMMER("夏",""),
AUTUMN("秋","9-11"),
WINTER("冬","12-2")
// 构造方法必须是 private
private Season(String name,String time){
this.name = name;
this.time = time;
}
// 方法的重写
@Override
public String getInfo() {
return "每年的"+ this.time +"月为"+ this.name +"季";
}
}
// 调用 (main方法中)
String str = Season.SUMMER.getInfo();
System.out.println(str)
------------------------------- " 结果 "
每年的6-8月为夏季
4) 常用方法 (继承与 Enum)
-
获取枚举类中的所有对象: 静态方法values( )
public enum Season { SPRING, SUMMER, AUTUMN, WINTER } Season[] arr = Season.values()
-
打印枚举类种的 对象名 toString() 、索引位置 ordinal()
for(int i = 0; i < arr.length; i++){ System.out.println("对象名:" + arr[i].toString()); System.out.println("索引:" + arr[i].ordinal()); } ------------------- " 结果 " 对象名:SPRING 索引:0 对象名:SUMMER 索引:1 对象名:AUTUMN 索引:2 对象名:WINTER 索引:3
-
将参数指定的
字符串
,转换为当前的枚举类对象
:静态方法valueOf( String str )注意:
字符串必须在枚举名之中,才能转换,否者会出现参数非法异常:
IllegaLargumentException
// valueOf为静态方法,直接用枚举类型调用 Season s = Season.valueOf("SUMMER"); System.out.println("转换过来的枚举名是:" + s.toString());
-
比较 枚举中的
枚举值(对象)
,在定义时的顺序:compareTo( E object )int num = A.compareTo(B);
num 的正负可以看成,索引的比较,Index(A) - Index(B):
- if (num > 0),说明 A在B之后
- if (num < 0),说明 A 再B之前
- if (num = 0),说明 A,B 在相同位置
// 比较 SUMMER、WINTER,在Season枚举中定义的先后顺序 int num = Season.SUMMER.compareTo(Season.WINTER);
注解
-
概念:从 Java5开始增加的 一种 引用数据类型
-
语法格式:
// 注解就是一个特殊的接口 访问修饰符 @interface 注解名{ 注解成员; }
-
注解,默认继承了
java.lang.annotation.Annotation
接口 -
自定义注解,举例
注解中成员属性的定义,看着像是抽象方法的定义,其实这就是成员变量的定义
// 自定义 MyAnnotation注解 public @interface MyAnnotation{ // 声明一个String类型的成员变量,名字为value String value(); // 加了 default, 在使用注解的时候,就可以使用默认值而不给value2赋值 String value2() default "18岁"; } // 注解的使用 @MyAnnotation(value = "小明" ) public class Person{ }
注意:
- 注解中,只有成员变量,无方法
- 只有一个成员时,建议使用成员变量 value
- 成员变量的类型,只能是:基本数据类型、String、Class、enum、Annotation类型
元注解
注解上的注解,或者说是:一种最基本的注解。
@Retention
描述的有效范围,或者生命周期, 取值为:
RetentionPolicy.SOURCE
:注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。RetentionPolicy.CLASS
:注解只被保留到编译进行的时候,它并不会被加载到JVM 中( 默认方式 )。RetentionPolicy.RUNTIME
:注解可以保留到程序运行的时候,它会被加载进入到JVM 中,所以在程序运行时可以获取到它们
@Retention(RetentionPolicy.SOURCE)// 下面的注解,只在源码中有效
@Retention(RetentionPolicy.CLASS )// 下面的注解,到编译阶段(字节码中)有效
@Retention(RetentionPolicy.RUNTIME )// 下面的注解,在解释执行阶段有效
public @interface MyAnnotation{
...;
}
RetentionPolicy
其实是一个 枚举类,作为 Retention注解的参数:
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
@Documented
被该注解标注的注解,在 javadoc
的时候就会把@Documented
标注的注解在文档中显示出来。(在生成文档时,默认是不包含注解的信息的)
@Target
被 @Target
标注的注解,可以修饰那些内容, 比如:
// 说明 @MyAnnotation,可以再注解上使用,也可以在构造方法上使用
@Target(ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR)
public @interface MyAnnotation{
...;
}
- @Target (ElementType.TYPE)——接口、类、枚举、注解
- @Target (ElementType.FIELD)——字段、枚举的常量
- @Target (ElementType.METHOD)——方法
- @Target (ElementType.PARAMETER)——方法参数
- @Target (ElementType.CONSTRUCTOR) ——构造函数
- @Target (ElementType.LOCAL_VARIABLE)——局部变量
- @Target (ElementType.ANNOTATION_TYPE)——注解
- @Target (ElementType.PACKAGE)——包
@Inherited
被 @Inherited
标注的注解,是否可以使用继承 (在被注解标记的那个类中)。举例:
@Inherited
public @interface MyAnnotation{
...;
}
@MyAnnotation
public class Person{
...
}
从上面的代码可以看到,MyAnnotation注解中,使用了 @Inherited
,所以被MyAnnotation注解 标注的 Person类
是可以做父类用于继承的。不然,不可以继承
@Repeatable
Java8新特性, @Repeatable标注的注解,在一个类上可以重复使用
预制注解
常见的有: