> 文章列表 > 反射与注解

反射与注解

反射与注解

反射与注解

      • 反射与注解
        • 一、类对象
        • 二、反射的用法
          • 2.1 反射操作属性
          • 2.2 反射操作方法
          • 2.3 反射访问构造方法
        • 三、设计模式
          • 3.1 工厂模式
            • 3.1.1 简单工厂
            • 3.1.2 抽象工厂
          • 3.2 单例模式
            • 3.2.1 饿汉(eager)模式
            • 3.2.2 懒汉(lazy)模式
        • 四、枚举
          • 4.1 枚举的基本使用
          • 4.2 枚举的高级使用
        • 五、注解
          • 5.1 注解的概念
          • 5.2 自定义注解
          • 5.3 元注解

反射与注解

反射就是通过一个类名(字符串)对类进行加载,获取类的信息,并对该类创建对象调用方法等操作。

一、类对象

类对象是描述类的信息的对象。

包含类有哪些属性、方法、构造方法等。

有三种获取方式:

public class TestMain {public static void main(String[] args) {// 第一种,通过对象获取Student stu = new Student();Class c1 = stu.getClass();// 第二种,通过类名获取Class c2 = Student.class;// 第三种,通过Class.forName方法获取try {Class c3 = Class.forName("com.qf.day23.Student");} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

二、反射的用法

当获得类对象后,可以获取该描述信息中的所有属性,方法,构造方法等信息,并且可以依据这些信息动态的创建对象,给属性赋值,调用方法。

2.1 反射操作属性
public class TestMain1 {public static void main(String[] args) {try {// 获取类对象信息Class c = Class.forName("com.qf.day23.Student");String name = c.getName();System.out.println(name); // 获取名称String simpleName = c.getSimpleName();System.out.println(simpleName); // 获取短名称System.out.println(c.getPackage()); // 获取包信息System.out.println(c.getSuperclass());// 获取父类信息System.out.println(Arrays.toString(c.getInterfaces()));// 获取接口信息// 通过无参构造方法创建对象Object obj = c.newInstance();// 获取所有属性Field[] fields = c.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName()); // 打印属性的名称// 把属性的访问限制修改为允许访问field.setAccessible(true);// 给属性赋值// 如果属性的类型是Stringif(field.getType() == String.class) {field.set(obj, "aaa");// 如果属性类型是int}else if(field.getType() == int.class) {field.set(obj, 20);}}// 获取单个属性Field field = c.getDeclaredField("age");System.out.println(field.getName());// 把属性的访问限制修改为允许访问field.setAccessible(true);// 得到该属性的值System.out.println(field.get(obj));} catch (Exception e) {e.printStackTrace();}}
}
2.2 反射操作方法
public class TestMain2 {public static void main(String[] args) {try {// 获取类对象信息Class c = Class.forName("com.qf.day23.Student");// 通过无参构造方法创建对象Object obj = c.newInstance();// 得到定义的所有方法Method[] methods = c.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName());// 判断是否以set开头if(method.getName().startsWith("set")) {// 得到方法的参数Class[] classes = method.getParameterTypes();if(classes.length == 1 && classes[0] == String.class) {// 调用方法method.invoke(obj, "aaa");}else if(classes.length == 1 && classes[0] == int.class) {// 调用方法,指定参数值method.invoke(obj, 18);}}}// 得到某一个方法Method method = c.getDeclaredMethod("say"); // 得到方法say()method.invoke(obj);// 得到某一个方法,指定方法的参数类型Method method1 = c.getDeclaredMethod("say", String.class); // 得到方法say(String)method1.invoke(obj, "张三");Method method2 = c.getDeclaredMethod("say", String.class, int.class); // 得到方法say(String, int)method2.invoke(obj, "张三", 20);Method method3 = c.getDeclaredMethod("say", c); // 得到方法say(Student)method3.invoke(obj, obj);} catch (Exception e) {e.printStackTrace();}}
}
2.3 反射访问构造方法
public class TestMain3 {public static void main(String[] args) {try {// 获取类对象信息Class c = Class.forName("com.qf.day23.Student");// 只能调用系统无参构造方法(不推荐使用)
//			Object obj = c.newInstance();
//			System.out.println(obj);// 得到所有的构造方法Constructor[] constructors = c.getConstructors();for (Constructor constructor : constructors) {System.out.println(constructor.getParameterCount());}// 得到一个构造方法Constructor constructor = c.getConstructor(String.class, int.class, String.class);// 通过构造方法来创建对象Object object = constructor.newInstance("张三", 20, "男");System.out.println(object);} catch (Exception e) {e.printStackTrace();}}
}

三、设计模式

3.1 工厂模式

使用工厂来实现对象的创建。

3.1.1 简单工厂

工厂直接提供各种对象的创建。

public class CarFactory {// 创建汽车public static Car createCar() {Car car = new Car();car.setBrand("五菱宏光");car.setColor("蓝色");car.setId("1");return car;}// 提供其他对象的创建方法
}public class TestMain {public static void main(String[] args) {Car car = CarFactory.createCar();}
}
3.1.2 抽象工厂

工厂类作为一个父类,定义了该工厂能够生产的对象,但是没有具体的实现,需要自己定义一个工厂的子类去生产相应的对象。

public abstract class AbstractFactory {// 生产汽车public abstract Car createCar();// 生产别的对象
}public class MyFactory extends AbstractFactory{@Overridepublic Car createCar() {Car car = new Car();car.setBrand("五菱宏光");car.setColor("蓝色");car.setId("1");return car;}
}public class TestMain {public static void main(String[] args) {AbstractFactory factory = new MyFactory();Car car2 = factory.createCar();}
}
3.2 单例模式

Singleton,表示该类只能创建一个对象。例如上面的工厂对象,在项目中就只需要创建一个。

3.2.1 饿汉(eager)模式

一开始就创建对象(能创建对象就创建)。

缺点是加载时创建对象,比较慢,使用时比较快。

public class EagerSingleton {private static EagerSingleton instance = new EagerSingleton();// 构造方法私有private EagerSingleton() {System.out.println("创建对象");}public static EagerSingleton getInstance() {return instance;}
}
3.2.2 懒汉(lazy)模式

一开始不创建对象,到使用时才创建。

优点是加载时不创建对象,会比较快,第一次使用时创建对象,相对较慢。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {System.out.println("创建对象");}public static LazySingleton getInstance() {if(instance == null) {instance = new LazySingleton();}return instance;}
}

上面的代码是有线程安全问题的,应该使用加锁的方式来解决线程安全问题。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {System.out.println("创建对象");}public static LazySingleton getInstance() {// 双重检测if(instance == null) {synchronized (LazySingleton.class) {if(instance == null) {instance = new LazySingleton();}}}return instance;}
}

使用内部类来实现。

public class LazySingleton1 {private LazySingleton1() {System.out.println("创建对象");}// 只有加载内部类时才会创建对象,而getInstance方法中才去加载了内部类public static LazySingleton1 getInstance() {return Inner.instance;}private static class Inner{public static LazySingleton1 instance = new LazySingleton1();}
}

四、枚举

枚举是一个特殊的类,是JDK1.5之后才有,是final的,通常用来列举一些值。

4.1 枚举的基本使用

以下是一个使用常量的案例:

public interface HeroStatus {int BING_DONG = 1;int XUAN_YUN = 2;int CHEN_MO = 3;
}public class Hero {public void setStatus(int status) {if(status == HeroStatus.BING_DONG) {System.out.println("英雄被冰冻");}else if(status == HeroStatus.CHEN_MO) {System.out.println("英雄被沉默");}else if(status == HeroStatus.XUAN_YUN) {System.out.println("英雄被眩晕");}}
}public class TestMain {public static void main(String[] args) {Hero hero = new Hero();// 用户调用时,可以使用常量,但是用户也可以直接写数字1,甚至可以写数字5,都不会出现编译错误hero.setStatus(HeroStatus.BING_DONG);}
}

为了让用户调用时,如果不用指定的常量,会出现编译错误,推荐使用枚举。

public enum HeroStatus {BING_DONG, XUAN_YUN, CHEN_MO;
}public class Hero {public void setStatus(HeroStatus status) {if(status == HeroStatus.BING_DONG) {System.out.println("英雄被冰冻");}else if(status == HeroStatus.CHEN_MO) {System.out.println("英雄被沉默");}else if(status == HeroStatus.XUAN_YUN) {System.out.println("英雄被眩晕");}}
}public class TestMain {public static void main(String[] args) {Hero hero = new Hero();// 此时使用数字就会报错hero.setStatus(HeroStatus.BING_DONG);}
}
4.2 枚举的高级使用

枚举是一个特殊的类,所以也能定义属性方法等。

public enum HeroStatus {// 需要放到最前面BING_DONG("1", "冰冻"),  // 构造方法XUAN_YUN("2", "眩晕"), CHEN_MO("3", "沉默");private String name;private String desc;public void say() {System.out.println("name=" + name + ", desc=" + desc);}private HeroStatus(String name, String desc) {this.name = name;this.desc = desc;}
}public class Hero {public void setStatus(HeroStatus status) {status.say();}
}public class TestMain {public static void main(String[] args) {Hero hero = new Hero();hero.setStatus(HeroStatus.CHEN_MO);}
}

五、注解

5.1 注解的概念

注解(Annotation),是在程序中的特殊标记,一般用来代替配置文件。

基本语法:

@类名,例如:@Override

定义方式:

public @interface 名称{

}

5.2 自定义注解

定义一个自动读取文件的注解。

工具类的定义:

@Target(ElementType.FIELD) // 表示该注解用在属性上
@Retention(RetentionPolicy.RUNTIME) // 表示该注解在运行时使用
public @interface ReadPath {// value表示默认属性,在赋值时,可以不写名称,如果是其他属性,赋值必须写名称String value() default "hello"; // 定义属性,给一个默认值
}
public class ReadFactory {public static Object createObject(String className) {try {Class c = Class.forName(className);Object obj = c.newInstance();// 读取txt中的属性Field[] fields = c.getDeclaredFields();for (Field field : fields) {// 判断属性上是否有注解@ReadPathif(field.isAnnotationPresent(ReadPath.class)) {// 得到该注解ReadPath readPath = field.getDeclaredAnnotation(ReadPath.class);// 得到该注解的值String path = readPath.value();System.out.println(path);// 判断值不为空if(path != null && path.length() > 0) {// 读取文件String content = readContent(path);field.setAccessible(true);field.set(obj, content);}}}return obj;} catch (Exception e) {e.printStackTrace();}return null;}private static String readContent(String path) {try (BufferedReader br = new BufferedReader(new FileReader(path));){StringBuilder builder = new StringBuilder();String str;while((str = br.readLine())!= null) {builder.append(str + "\\n");}return builder.toString();} catch (Exception e) {e.printStackTrace();}return null;}
}

使用:

public class ReadTxt {@ReadPath("C:\\\\Users\\\\wangliang\\\\Desktop\\\\1.txt")private String content;public void read() {System.out.println(content);}
}
public class TestMain {public static void main(String[] args) {ReadTxt txt = (ReadTxt)ReadFactory.createObject("com.qf.day23.i.ReadTxt");txt.read();}
}
5.3 元注解

元注解:用在注解上的注解。

元数据:描述数据的数据。(一般指配置文件,xml等)

自定义的注解上至少应该有以下元注解:

@Target(ElementType.METHOD):表示自定义的注解使用的位置(方法、属性、变量、类等)
@Retention(RetentionPolicy.SOURCE):表示自定义的注解使用的时机(源代码、运行时)

还有两个系统提供的元注解:

@Inherited:表示该注解是否可以继承(组合注解)

@Documented:表示该注解是否生成文档