第11章 枚举&注解&内部类
一、枚举
概念
枚举类型是Java 5中新增特性的⼀部分,它是⼀种特殊的数据类型,之所以特殊是因为它既是⼀种类
(class)类型却⼜⽐类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性
以及便捷性。
枚举在各个语⾔当中都有着⼴泛的应⽤,通常⽤来表示诸如颜⾊、⽅式、类别、状态等等数⽬有限、形
式离散、表达⼜极为明确的量。Java从JDK5开始,引⼊了对枚举的⽀持。
为什么使用枚举
在枚举出现之前,如果想要表示⼀组特定的离散值,往往使⽤⼀些常量。例如:
public class Entity { public static final int VIDEO = 1;//视频 public static final int AUDIO = 2;//⾳频 public static final int TEXT = 3;//⽂字 public static final int IMAGE = 4;//图⽚ private int id; private int type; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getType() { return type; } public void setType(int type) { this.type = type; } }
例如,针对上述的Entity类,如果要对Entity对象的type属性进⾏赋值,⼀般会采⽤如下⽅法:
Entity e = new Entity();
e.setId(10);
//e.setType(2);
//⽽这样的话,问题⼜来了。这样做,客户端必须对这些常量去建⽴理解,才能了解如何去使⽤这个东西。
//说⽩了,在调⽤的时候,如果⽤户不到Entity类中去看看,还真不知道这个参数应该怎么传、怎
么调
e.setType(Entity.AUDIO);
缺点
(1)代码可读性差、易⽤性低。由于setType()⽅法的参数是int型的,在阅读代码的时候往往会让读者
感到⼀头雾⽔,根本不明⽩这个2到底是什么意思,代表的是什么类型。
(2)类型不安全。在⽤户去调⽤的时候,必须保证类型完全⼀致,同时取值范围也要正确。像是
setType(-1)这样的调⽤是合法的,但它并不合理,今后会为程序带来种种问题。
(3)耦合性⾼,扩展性差。假如,因为某些原因,需要修改Entity类中常量的值,那么,所有⽤到这些
常量的代码也就都需要修改——当然,要仔细地修改,万⼀漏了⼀个,那可不是开玩笑的。同时,这样
做也不利于扩展。
使用枚举
枚举(在Jave中简称为enum)是⼀个特定类型的类。所有枚举都是Java中的新类
java.lang.Enum的隐式⼦类。此类不能⼿⼯进⾏⼦类定义。⼀个简单的枚举可以是这样:
public enum TypeEnum { VIDEO,AUDIO,TEXT,IMAGE; }
上⾯的Entity类就可以改成这样:
public class Entity2 { private int id; private TypeEnum type; public int getId() { return id; } public void setId(int id) { this.id = id; } public TypeEnum getType() { return type; } public void setType(TypeEnum type) { this.type = type; } }
在为Entity对象赋值的时候,就可以这样:
Entity2 e = new Entity2(); e.setId(10); e.setType(TypeEnum.AUDIO);
在调⽤setType()时,可选值只有四个,否则会出现编译错误,因此可以看出,枚举是类型安全的,不会
出现取值范围错误的问题。同时,客户端不需要建⽴对枚举中常量值的了解,使⽤起来很⽅便,并且可
以容易地对枚举进⾏修改,⽽⽆需修改客户端。如果常量从枚举中被删除了,那么客户端将会失败并且
将会收到⼀个错误消息。
在Java中⼀个枚举就是⼀个类,它也可以有属性和⽅法,并且实现接⼝。只是所有的枚举都继承⾃
java.lang.Enum类,因此enum不可以再继承其他的类。
下⾯给出在枚举中声明属性和⽅法的示例:
public enum TypeEnum { VIDEO(1), AUDIO(2), TEXT(3), IMAGE(4); int value; TypeEnum(int value) { this.value = value; } public int getValue() { return value; } }
如果要为每个枚举值指定属性,则在枚举中必须声明⼀个参数为属性对应类型的构造⽅法(不能
是public)。否则编译器将给出The constructor TypeEnum(int, String) is undefined的错误。
在此例中,属性为int型,因此构造⽅法应当为int型。除此之外,还可以为枚举指定多个属性
枚举构造器为什么不能是public?
如果其含有public构造器,那么在类的外部就可以通过这个构造器来新建实例,显然这时实例的数量和
值就不固定了,这与定义枚举类的初衷相⽭盾,为了避免这种形象,就对枚举类的构造器默认使⽤
private修饰。如果为枚举类的构造器显式指定其它访问控制符,则会编译出错。、
public enum TypeEnum { VIDEO(1, "视频"), AUDIO(2, "⾳频"), TEXT(3, "⽂本"), IMAGE(4, "图像"); int value; String name; TypeEnum(int value, String name) { this.value = value; this.name = name; } public int getValue() { return value; } public String getName() { return name; } }
二、注解
所有注解 继承自java.lang.annotation.Annotation接口
package enum_annotation_innerclass.anno; public @interface MyAnno { //属性列表 int value(); String[] abc(); }
反编译: javap MyAnno.class
Compiled from "MyAnno.java"
public interface enum_annotation_innerclass.anno.MyAnno extends java.lang.annotation.Annotation { public abstract int value(); public abstract java.lang.String[] abc(); }
属性列表本质上是接口抽象方法
返回值必须为以下类型:
- 八大基本数据类型
- String
- 枚举
- 注解
- 以及以上类型的数组
使用注解时若没有默认值要赋值,若有 default则不用赋值
若只有一个属性,可以不用写属性名,直接赋值
数组时,若只有一个元素,则可以不写{}
@Target:描述注解能够作⽤的位置
ElementType 取值:
ElementType.TYPE 指定注解只能修饰类或者接⼝ --重点
ElementType.FIELD 指定注解只能修饰成员属性 --重点
ElementType.METHOD 指定注解只能修饰⽅法 --重点
ElementType.PARAMETER 指定注解只能修饰⽅法的参数
ElementType.CONSTRUCTOR 指定注解只能修饰构造⽅法
ElementType.LOCAL_VARIABLE 指定注解只能修饰局部变量
ElementType.TYPE_USE 指定注解能修饰所有的 --JDK1.8后拥有
的
@Retention:描述注解被保留的阶段
RetentionPolicy.SOURCE 注解信息保留在源⽂件中
RetentionPolicy.CLASS 保留在class⽂件中
RetentionPolicy.RUNTIME 注解信息在运⾏时保留,搭配反射使⽤ --重点
元注解:注解的注解
package enum_annotation_innerclass.anno; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE_PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(CLASS) @Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }) /** * @author gcl15 * */ public @interface AllAnno { }
@Documented
用来生成文档
eclipse: project->Generate Javadoc->VM options设置为 -encoding UTF-8 -charset UTF-8 ->完成
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
@Retention
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
枚举类:public enum RetentionPolicy { //保留策略
SOURCE, //源码
CLASS, //编译期
RUNTIME //运行期
}
@Target
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
public enum ElementType { /** 类,接口(包含注解),枚举*/ TYPE, /**字段声明,包括注解常量*/ FIELD, /** 方法声明*/ METHOD, /** 入参*/ PARAMETER, /** 构造方法*/ CONSTRUCTOR, /** 本地变量声明(方法中的局部变量))*/ LOCAL_VARIABLE, /** 注解类型声明*/ ANNOTATION_TYPE, /** 包 */ PACKAGE, /** *类型参数 */ TYPE_PARAMETER, /** */ TYPE_USE }
三、内部类
什么是内部类
可以将一个类的定义放在另一个类的内部,这就是内部类,广义上我们将内部类分为四种:成员内部类,静态内部类,局部(方法)内部类,匿名内部类。
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个接口的实现,对于内部类都没有影响。
语法:
public class Outer{ class Inner{ } }
成员内部类
外部类、内部类
package enum_annotation_innerclass.innerclass; public class Outer { private int outerVar = 1; private int commonVar = 2; private static int outerStaticVar = 3; public void outerMethod() { System.out.println("外部类成员方法outerMethod"); } public static void outerStaticMethod() { System.out.println("外部类静态方法outerStaticMethod"); } public Inner getInstance() { Inner inner = new Inner(); return inner; } // 成员内部类 public class Inner { private int commonVar = 20; public Inner() { super(); } /** * 成员方法,访问外部类信息(属性和方法) */ public void innerShow() { System.out.println("commonVar:" + commonVar); System.out.println("外部类commonVar:" + Outer.this.commonVar); System.out.println("外部类的成员变量outerVar:" + outerVar); System.out.println("外部类的静态变量outerStaticVar:" + outerStaticVar); // 外部类的方法 outerMethod(); outerStaticMethod(); } } }
其他类使用内部类
package enum_annotation_innerclass.innerclass; public class Other { public static void main(String[] args) { // 创建成员内部类的对象和调用方法 Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); System.out.println(outer.getInstance()); inner.innerShow(); } } enum_annotation_innerclass.innerclass.Outer$Inner@7852e922 commonVar:20 外部类commonVar:2 外部类的成员变量outerVar:1 外部类的静态变量outerStaticVar:3 外部类成员方法outerMethod 外部类静态方法outerStaticMethod
小结:成员内部类当成外部类的成员信息存在
- 可以是任何的访问修饰符
- 外部类如何访问内部类信息,必须new之后通过.操作符访问
- 内部类可以直接使用外部类的任何信息,如果属性或者方法发生冲突,调用 外部类.this.属性/方法
静态内部类
package enum_annotation_innerclass.innerclass; import enum_annotation_innerclass.innerclass.Outer.Inner; public class StaticOuter { private int outerVar = 1; private int commonVar = 2; private static int outerStaticVar = 3; static { System.out.println("StaticOuter静态代码块被执行了"); } public void outerMethod() { System.out.println("外部类成员方法outerMethod"); } public static void outerStaticMethod() { System.out.println("外部类静态方法outerStaticMethod"); } // 外部类如何同内部类打交道 public static void callInner() { System.out.println(StaticInner.innerStaticVar); StaticInner.innerStaticShow(); } public static class StaticInner { private int innerVar = 10; private int commonVar = 20; private static int innerStaticVar = 30; static { System.out.println("StaticInner静态代码块被执行了"); } public void innerShow() { System.out.println("innerVar:" + innerVar); System.out.println("内部的commonVar:" + commonVar); System.out.println("outerStaticVar:" + outerStaticVar); outerStaticMethod(); } public static void innerStaticShow() { /* * outerStaticMethod(); System.out.println("outerStaticVar:"+outerStaticVar); */ } } }
使用
package enum_annotation_innerclass.innerclass; import enum_annotation_innerclass.innerclass.StaticOuter.StaticInner; public class Other { public static void main(String[] args) { // 创建成员内部类的对象和调用方法 // Outer outer = new Outer(); // Outer.Inner inner = outer.new Inner(); // System.out.println(outer.getInstance()); // inner.innerShow(); //访问静态内部类的静态方法,静态内部类加载,外部类未被加载,独立存在,不依赖于外部类 StaticOuter.StaticInner.innerStaticShow(); //访问静态内部类中的成员方法 // StaticOuter.StaticInner staticInner = new StaticOuter.StaticInner(); // staticInner.innerShow(); } }
小结:和成员内部类对⽐理解(区别异同)
- 作为静态成员属性存在,可以被任意的权限修饰符修饰。
- 静态内部类的方法只能访问外部类的static关联的信息。
- 利用 外部类.内部类 引用 = new 外部类.内部类(); 利用 引用.成员属性/方法 调用
Outer.Inner oi = new Outer.Inner();
oi.innerShow();
- 访问内部类的静态信息,直接外部类.内部类.静态信息就可以了
//访问静态内部类的静态⽅法,Inner类被加载,此时外部类未被加载,独⽴存在,不依赖于外围类。
Outer.Inner.innerStaticShow();
- 静态内部类可以独⽴存在,不依赖于其他外围类。
匿名内部类
1.定义接口
package enum_annotation_innerclass.innerclass; public interface IAnimal { public void speak(); }
2.匿名内部类使用
package enum_annotation_innerclass.innerclass; public class IAnimalTest { public static IAnimal getInnerInstance(String speak) { return new IAnimal() { @Override public void speak() { System.out.println(speak); } }; } public static void main(String[] args) { IAnimalTest.getInnerInstance("汪汪汪!!!").speak(); } }
反编译javap IAnimalTest$1.class后:
class enum_annotation_innerclass.innerclass.IAnimalTest$1 implements enum_annotation_innerclass.innerclass.IAnimal { public int aaa; enum_annotation_innerclass.innerclass.IAnimalTest$1(java.lang.String); public void speak(); }
3.小结【匿名内部类常常被⽤来重写某个或某些⽅法】
- 匿名内部类是没有访问修饰符的。
- 使用匿名内部类时,这个new之后的类首先是要存在的,其次我们要重写new后的类的某个或某些方法。
第12章 反射
概念
反射:将类的各个组成部分封装为其他对象,这就是反射机制。
好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可拓展性。
获取Class对象的方式
- Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
- 类名.class:通过类名的属性class获取,多用于参数的传递。
- 对象.getClass():getClass()方法在Object类中定义。
结论:同一个字节码文件,在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取Class对象都是同一个。
package reflect; public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } public void eat() { System.out.println("eat()......"); } }
package reflect; public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub Class<?> aClass = Class.forName("reflect.Person"); System.out.println(aClass.toString()); Class<Person> personClass = Person.class; System.out.println(personClass); Person p = new Person(); Class<? extends Person> clazz = p.getClass(); System.out.println(clazz); System.out.println("(aClass == personClass)"+(aClass == personClass)); System.out.println("(personClass == clazz)"+(personClass == clazz)); } } class reflect.Person class reflect.Person class reflect.Person (aClass == personClass)true (personClass == clazz)true
Class对象的功能
获取成员变量
- Field[] getFields() :获取所有public修饰的成员变量
- Field getField(String name) 获取指定名称的 public修饰的成员变量
- Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name)获取指定的成员变量,不考虑修饰符
Field:成员变量
- 设置值 void set(Object obj, Object value)
- 获取值 get(Object obj)
- 忽略访问权限修饰符的安全检查(暴⼒反射) setAccessible(true)
package reflect; import java.lang.reflect.Field; public class ReflectDemo2 { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Person p = new Person(); //第一步,获取class类对象 Class<Person> personClass = Person.class; Field[] fields = personClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("-----------------------------------------"); Field i = personClass.getField("i"); i.set(p, 123); System.out.println("i的值:"+i.get(p)); System.out.println("i:"+i); System.out.println("-----------------------------------------"); Field name = personClass.getDeclaredField("name"); //忽略访问修饰符的检查 name.setAccessible(true); //设值 name.set(p,"liubowen"); //取值 System.out.println("name的值:"+name.get(p)); System.out.println("name:"+name); System.out.println("-----------------------------------------"); } }
获取构造函数
- Constructor[] getConstructors() 获取所有的公共的构造方法
- Constructor getConstructor(Class...parameterTypes) 获取指定的公共的构造方法
- Constructor getDeclaredConstructor(Class...parameterTypes) 获取指定的声明的构造方法
- Constructor[] getDeclaredConstructors() 获取所有的声明的构造方法
Constructor:构造方法
创建对象:T newInstance(Object... initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
实例:
package reflect; public class Person { private int age; private String name; public int i; public Person() {} private Person(int age) { super(); this.age = age; } public Person(int age, String name) { super(); this.age = age; this.name = name; } public void eat() { System.out.println("eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectDemo3 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // TODO Auto-generated method stub /*Constructor<?>[] getConstructors() Constructor<T> getConstructor(Class<?>...parameterTypes) Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) Constructor<?>[] getDeclaredConstructors()*/ Class<Person> personClass = Person.class; //获取有参的构造方法 Constructor<Person> constructor = personClass.getConstructor(int.class,String.class); System.out.println(constructor); //创建对象1 Person person = constructor.newInstance(28,"博文"); System.out.println(person.toString()); //获取无参的构造方法 Constructor<Person> constructor2 = personClass.getConstructor(); System.out.println("constructor2:"+constructor2); //创建对象2 Person person2 = constructor2.newInstance(); System.out.println("person2:"+person2); //创建对象3 Person person3 = personClass.newInstance(); System.out.println("person3:"+person3); System.out.println("------------------------------------------"); //获取Constructor数组 Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors(); System.out.println(declaredConstructors.toString()); for (Constructor<?> constructor3 : declaredConstructors) { System.out.println(constructor3); } System.out.println("------------------------------------------"); //构造器暴力访问 } }
获取成员方法
- Method[] getMethods() 获取类里面所有的public方法,包括父类里面的public方法
- Method getMethod(String name,Class... parameterTypes) 获取指定的public方法
- Method[] getDeclaredMethods() 获取类里面声明的所有的方法,不包括父类的方法
- Method getDeclaredMethod(String name,Class... parameterTypes) 获取类里面声明的指定方法,不包括父类的方法
Method:方法对象
执行方法:Object invoke(Object obj, Object... args)
获取方法名称: String getName();
示例代码:
package reflect; public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } private Person(int age) { super(); this.age = age; } public void eat() { System.out.println("eat()......"); } public void eat(String who) { System.out.println(who+" eat()......"); } private void eat(String who,String other) { System.out.println(who+other+" eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }
package reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo4 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<Person> personClass = Person.class; //获取指定名称的方法 Method eat = personClass.getMethod("eat"); Person p = new Person(); eat.invoke(p); //获取指定名称,并且带参数的方法 Method eat2 = personClass.getMethod("eat", String.class); //执行带参数的方法 eat2.invoke(p, "lbw"); System.out.println("------------------------------------------"); //获取所有public修饰的方法 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method); // System.out.println(method.getName()); } System.out.println("------------------------------------------"); //获取类名 String name = personClass.getName(); System.out.println("类名:"+name); } }
在程序中使用和解析注解
在程序中使用 注解:获取注解中定义的属性值
- 获取注解定义的位置的对象(Class,Method,Field)
- 判断注解是否存在 isAnnotationPresent(Class)
- 获取指定的注解 getAnnotation(Class)
- 调用注解中的抽象方法获取配置的属性值
示例代码:
package reflect; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RUNTIME) public @interface MyAnno4 { int age() default 0; }
package reflect; @MyAnno4(age = 28) public class Person { private int age; private String name; public int i; public Person() {} public Person(int age, String name) { super(); this.age = age; this.name = name; } private Person(int age) { super(); this.age = age; } @MyAnno4(age = 787878) public void eat() { System.out.println("eat()......"); } public void eat(String who) { System.out.println(who+" eat()......"); } private void eat(String who,String other) { System.out.println(who+other+" eat()......"); } @Override public String toString() { return "Person [age=" + age + ", name=" + name + ", i=" + i + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }
package reflect; import java.lang.reflect.Method; public class AnnotationDemo3 { public AnnotationDemo3() { // TODO Auto-generated constructor stub } public static void main(String[] args) throws NoSuchMethodException, SecurityException { Class<Person> personClass = Person.class; System.out.println(personClass.isAnnotationPresent(MyAnno4.class)); MyAnno4 anno4 = personClass.getAnnotation(MyAnno4.class); int age = anno4.age(); System.out.println("age:"+age); System.out.println("------------------------------------------"); Method test = personClass.getMethod("eat"); System.out.println(test.isAnnotationPresent(MyAnno4.class)); //获取方法上定义的注解 if(test.isAnnotationPresent(MyAnno4.class)) { System.out.println("est方法有定义注解"); MyAnno4 anno42 = test.getAnnotation(MyAnno4.class); int age2 = anno42.age(); System.out.println("age2:"+age2); } } }