😊😊😊😊创作不易,各位看官点赞收藏.
Java之枚举、注解与反射
1、枚举类
通常对象只有有限个并且确定时,我们通常使用枚举类来表示这些类。当我们需要的定义一组常量时,强烈建议使用枚举类。
1.1、自定义枚举类
? 在JDK5.0 之前使用枚举类都是自定义枚举类,自己编写枚举类。
class Season{
private final String name;
private Season(String name){
this.name = name;
}
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("冬天");
public String getName() {
return name;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
'}';
}
}
1.2、Enum 枚举类
? 在JDK5.0 之后可以使用Enum 关键字来定义一个枚举类。
public enum Pay {
WECHAT("微信"),
ALIPAY("支付宝"),
CARD("银行卡"),
CASH("现金");
private final String type;
private Pay(String type){
this.type = type;
}
public String getType() {
return type;
}
@Override
public String toString() {
return "Pay{" +
"type='" + type + '\'' +
'}';
}
}
注意:
enum 中提供的对象,实质省略了public static final 和 new Pay 。enum 自动继承java.lang.Enum 这个类,不是继承至Object类。enum 如果没有重写toString 方法,那么打印的是对象名称。
1.3、枚举类常用方法
public static void main(String[] args) {
Pay[] values = Pay.values();
for (Pay value : values) {
System.out.println(value);
}
Pay wechat = Pay.valueOf("WECHAT");
System.out.println(wechat);
}
1.4、枚举类实现接口
方式一:在枚举类中所有对象都执行至一个重写接口的方法。
interface Test{
void show();
}
public enum Pay implements Test{
@Override
public void show() {
System.out.println("所有对象执行同一个方法");
}
}
方式二:每一个枚举对象都可以重写接口中的方法,这样每一个枚举对象执行的方法就会执行自己对应不同重写方法。
public enum Pay implements Test{
WECHAT("微信"){
@Override
public void show() {
System.out.println("使用微信支付");
}
},
ALIPAY("支付宝"){
@Override
public void show() {
System.out.println("使用支付宝支付");
}
},
CARD("银行卡"){
@Override
public void show() {
System.out.println("使用银行卡支付");
}
},
CASH("现金"){
@Override
public void show() {
System.out.println("使用现金支付");
}
};
}
2、注解
注解(annotation):不是程序的本身,但是可以对程序作出解释可以被其它程序读取,是一种特殊标记。
2.1、文档注解
? 生成文档相关的注解。
public class Demo01 {
public static void main(String[] args) {
}
}
2.2、内置注解
注解名 | 解释 |
---|
@Override | 注释于方法上,用于注解该方法是重写父类的方法 | @Deprecated | 注释于类和方法上,注释该类和方法不推荐使用,但是可以使用 | @SuppressWarnings | 用于镇压警告提示,作用于方法和类上,被注解的块中的警告提示全部消失 |
2.3、元注解
元注解:用于注解其它的注解,用来对其它的注解类型作进行说明。
注解名 | 解释 |
---|
@Target | 用于描述注解的作用范围,参数是一个ElementType 枚举类型的数组 | @Retention | 用于描述注解的生命周期,参数是一个RetentionPolicy 枚举类型 | @Documented | 设置注解是否包含到javadoc 文档中,设置就包含 | @Inherited | 设置子类是否继承父类的注解,设置了就继承父类的注解 |
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.PACKAGE})
@Retention(value = RetentionPolicy.CLASS)
@Documented
@Inherited
@interface MyAnnotation{
}
2.4、自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1 {
int id() default 100;
String name() default "张三";
String[] hobby() default {"篮球","羽毛球"};
}
注解和反射一起使用才有意义,可以通过反射获取注解中的信息。
@MyAnnotation1(id = 1,name = "李四",hobby = {"乒乓球","棒球"})
public void test(){}
注意:
- 注解参数类型:基本数据类型、类、
enum 、注解及它们的数组形式。 - 如果没有指定默认值,在使用注解的时候必须传入参数。
- 如果只有一个参数,建议使用value,这样在使用注解时可以省略value
2.5、Java8 注解新特性
可重复注解:在一个地方可以使用多个注解。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest {
String name() default "";
String vale();
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTests {
AnnotationTest[] value();
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(AnnotationTests.class)
public @interface AnnotationTest {
String name() default "";
String vale();
}
@AnnotationTest(vale = "张三")
@AnnotationTest(vale = "李四")
public void test1(){
}
注意 AnnotationTests 除了没有Retention 注解,其它的元注解都应该和AnnotationTest 的元注解相同。
类型注解:关于@target 注解,在jdk1.8 新增了TYPE_PARAMETER 和TYPE_USE 表示注解可以使用到变量类型上。
public void test2(){
ArrayList<@AnnotationTest(vale = "张三") String> array = new ArrayList<>();
class B<@AnnotationTest(vale = "李四") T>{
}
}
3、反射(Reflection)
反射被视为java 作为动态语言的关键,反射机制允许程序执行期间通过Reflection 取得任何类得内部信息,包括私有属性,并能直接操作任意对象的内部属性和方法。
加载完类后,在堆内存的方法区中就产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了类的完整结构,我们可以通过这个对象来操作这个类。
Java反射机制有什么作用?
- 在运行时可以判断任意一个对象的所属类。
- 在运行时我们可以去创建任何一个类的对象。
- 在运行时可以判断一个类具有的所有属性和方法。
- 在运行时可以获取泛型信息。
- 在运行时可以调用一个对象的属性和方法。
- 在运行时可以获取注解中的值。
3.1、Class 类
Class:是java.lang 包下的一个类,在虚拟机加载一个class字节码文件到内存中这称为类加载,加载到内存中的类我们称为运行时类,这个类就可以作为Class的一个实例对象。这个对象中包含了这个类的所有结构,这个类的所有实例都有一个相同的Class类对应的对象。你无论创建多少个实例,但是Class类对应实例对象只有一个。(万事万物皆对象)
哪些类型可以拥有Class类的实例对象?
- class类(普通类、接口、内部类)。
- 数组、枚举、注解。
- void、基本数据类型。
public static void main(String[] args) {
Class<Integer> integerClass = int.class;
Class<Void> voidClass = void.class;
Class<Override> overrideClass = Override.class;
Class<? extends ElementType> c1 = ElementType.METHOD.getClass();
Class<ElementType> c2 = ElementType.class;
Class<?> c3 = null;
try {
c3 = Class.forName("java.lang.annotation.ElementType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
int[] array = new int[10];
Class<int[]> c4 = int[].class;
Class<? extends int[]> c5 = array.getClass();
Class<String> c6 = String.class;
Class<? extends String> c7 = new String("你好").getClass();
try {
Class<?> c8 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Class<String> c1 = String.class;
Class<? extends String> c2 = "".getClass();
Class<?> c3 = null;
try {
c3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
ClassLoader classLoader = Demo02.class.getClassLoader();
Class<?> c4 = null;
try {
c4 = classLoader.loadClass("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
}
类加载器加载配置文件:
方式一的资源相对路径相对于当前项目下,而方式二的资源相对路径相对于src目录下。推荐使用第二种方式。
public static void main(String[] args){
Properties properties = new Properties();
try (FileInputStream inputStream = new FileInputStream("注解和反射\\src\\jdbc.properties");){
properties.load(inputStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.println("user: "+user+",password: "+password);
} catch (IOException e) {
e.printStackTrace();
}
ClassLoader classLoader = Demo03.class.getClassLoader();
try ( InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");){
properties.load(resourceAsStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.println("user: "+user+",password: "+password);
} catch (IOException e) {
e.printStackTrace();
}
}
3.2、创建运行时类的对象
public static void main(String[] args) {
Class<String> c = String.class;
try {
String s = c.newInstance();
System.out.println(s);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
注意:
- 使用反射来创建运行时类的对象,实质还是使用的运行时类的无参构造器来创建对象。
- 如果运行时类中没有无参构造器会报错,创建好对象后属性值是默认值。
- 类构造器的权限需要满足当前程序。
3.3、获取运行时类的结构
我们通过反射获取的Class对象中包含了这个类的所有信息,名称、属性、方法等。
public class Student {
private int id;
public String name;
public Student() {}
private Student(String name){
this.name = name;
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
private void method1(){
System.out.println("我是一个没有返回值没有参数的private方法");
}
public String method2(){
System.out.println("我是一个有返回值没有参数的public方法");
return "返回值2";
}
public String method3(String value){
System.out.println(value);
System.out.println("我是一个有返回值有参数的public方法");
return "返回值3";
}
}
方法 | 解释 |
---|
getName() | 获取类的全限定类名。 | getSimpleName() | 获取类的类名。 | getField(参数) | 获取类指定的public修饰的属性,参数是属性名的字符串。返回值是Field类型。 | getDeclaredField(参数) | 获取类的指定的属性不管是什么修饰,参数是属性名的字符串。返回值是Field类型。 | getFields() | 获取类中的所有public修饰的属性,返回Field类型的数组。(包含继承至父类的属性) | getDeclaredFields() | 获取类中的所有属性(任何权限),返回Field类型的数组。(不包含继承至父类的属性) | getMethod(参数1, 参数2) | 获取类及父类中指定的public修饰的方法,参数1是方法名字符串,后面的参数是方法的参数的Class对象,可以有多个,返回值是Method类型。 | getDeclaredMethod(参数1,参数2) | 和上个方法类似,但是这个方法的修饰不限制,但是只能获取本类的方法。 | getMethods() | 获取类及父类中的所有public修饰的方法,返回值是一个Method的数组。 | getDeclaredMethods() | 获得本类中的所有方法,返回值是一个Method的数组。 | getConstructors() | 获得本类中public修饰的构造方法,返回值是一个Constructor的数组。 | getDeclaredConstructors() | 获得本类中的所有构造方法,返回值是一个Constructor的数组。 |
获取类的属性结构:
public static void main(String[] args) {
Class<Student> c = Student.class;
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
Field[] declaredFields = c.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
Field field = fields[0];
int modifiers = field.getModifiers();
System.out.println(Modifier.toString(modifiers));
Class<?> type = field.getType();
System.out.println(type);
String name = field.getName();
System.out.println(name);
}
获取类方法结构
public static void main(String[] args) {
Class<Student> c = Student.class;
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
Method[] declaredMethods = c.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
Method method = declaredMethods[0];
int modifiers = method.getModifiers();
System.out.println(Modifier.toString(modifiers));
Class<?> returnType = method.getReturnType();
System.out.println(returnType.getSimpleName());
String name = method.getName();
System.out.println(name);
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println(myAnnotation.value());
System.out.println(myAnnotation.name());
}
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType.getSimpleName());
}
}
获取类构造器的结构
public static void main(String[] args) {
Class<Student> c = Student.class;
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
获取类实现的接口:
public static void main(String[] args) {
Class<Student> c = Student.class;
Class<?>[] interfaces = c.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println(i);
}
Class<? super Student> superclass = c.getSuperclass();
System.out.println(superclass);
Package p = c.getPackage();
System.out.println(p);
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
3.4、调用运行时类指定结构
3.4.1、调用属性
public static void main(String[] args) {
Class<Student> c = Student.class;
Student student = null;
try {
student = c.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
try {
Field name = c.getField("name");
name.set(student,"张三");
String nameFiled = (String) name.get(student);
System.out.println(nameFiled);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
try {
Field id = c.getDeclaredField("id");
id.setAccessible(true);
id.set(student,10001);
int idFiled = (int) id.get(student);
System.out.println(idFiled);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
3.4.2、调用方法
public static void main(String[] args) {
Class<Student> c = Student.class;
Student student = null;
try {
student = c.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
try {
Method method = c.getDeclaredMethod("method1", String.class);
method.setAccessible(true);
Object result = method.invoke(student, "参数值");
System.out.println(result);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
try {
Method method = c.getDeclaredMethod("staticMethod", String.class);
method.setAccessible(true);
Object result = method.invoke(Student.class, "参数值");
System.out.println(result);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
3.4.3、调用构造器
public static void main(String[] args) {
Class<Student> c = Student.class;
try {
Constructor<Student> constructor = c.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Student student = constructor.newInstance("张三");
System.out.println(student);
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
3.5、反射应用之动态代理
代理模式:使用一个代理对象将对象包装起来,然后使用代理对象取代原始对象。任何原始对象的调用都要通过代理对象,代理对象决定了是否以及何时去调用到原始对象上。代理模式分为静态代理、动态代理。
3.5.1、静态代理
例如客户想要租房子,他可以通过中介去租房子。这样客户就是被代理类,中介就是代理类,他们共同事情都是租房子,租房子就是这个公共的接口。
公共接口:
public interface ProxyInterface {
void rent();
}
被代理对象(顾客):
public class ProxyImpl implements ProxyInterface {
@Override
public void rent() {
System.out.println("我是顾客,我需要租房子");
}
}
代理对象(中介):
public class ProxyObject implements ProxyInterface {
private final ProxyInterface proxyImpl;
public ProxyObject(ProxyInterface proxyImpl) {
this.proxyImpl = proxyImpl;
}
@Override
public void rent() {
this.proxyImpl.rent();
System.out.println("我是中介,我正在帮顾客租房子");
System.out.println("我已经帮顾客租到房子了");
}
}
代理测试:
public static void main(String[] args) {
ProxyImpl proxyImpl = new ProxyImpl();
ProxyObject proxyObject = new ProxyObject(proxyImpl);
proxyObject.rent();
}
3.5.2、动态代理
静态代理是在编译期间都确定下来了,不利于程序的扩展。同时一个代理类只能为一个接口代理,如果需要多个代理对象就需要创建多个代理类。所以为了解决这个问题就产生了动态代理,使用一个代理类来完成所有代理接口,这就需要反射来实现。
创建一个类,根据传入的不同的代理对象来生成对应不同的代理对象。
public class ProxyFactory implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类正在执行方法");
Object result = method.invoke(this.target, args);
System.out.println("代理类完成代理");
return result;
}
}
创建一个租房被代理类:
public interface RentInterface {
void rent();
}
public class RentClient implements RentInterface {
@Override
public void rent() {
System.out.println("我是顾客,我需要租房子");
}
}
创建一个衣服被代理类:
public interface ClothInterface {
String make();
}
public class ClothImpl implements ClothInterface {
@Override
public String make() {
System.out.println("我是衣服被代理类,我正在制造衣服");
return "ok";
}
}
动态代理测试:
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
RentInterface rentClient = new RentClient();
proxyFactory.setTarget(rentClient);
RentInterface proxy = (RentInterface) proxyFactory.getProxyInstance();
proxy.rent();
ClothImpl clothImpl = new ClothImpl();
proxyFactory.setTarget(clothImpl);
ClothInterface clothProxy = (ClothInterface) proxyFactory.getProxyInstance();
String make = clothProxy.make();
System.out.println(make);
}
|