> 文章列表 > Java阶段二Day06

Java阶段二Day06

Java阶段二Day06

Java阶段二Day06

文章目录

  • Java阶段二Day06
    • JAVA反射机制
    • 获取一个类的类对象方式
    • 反射对象
    • 反射机制实例化对象
    • 操作特定构造器进行对象实例化
    • 使用反射机制调用方法
    • 通过反射机制访问类的私有成员
    • 反射机制中访问注解
      • 注解
      • 判断一个类是否被某个注解标注
      • 判断一个方法是否被指定注解标注
      • 访问注解参数

JAVA反射机制

  • 反射是java的动态机制,允许程序在"运行期间"再确定如:对象的实例化,方法的调用,属性的操作等。
  • 反射机制可以提高代码的灵活性和适应性。但是会带来较多的系统开销和较慢的运行效率
  • 因此反射机制不能过度被依赖。

反射机制使用的第一步:获取待操作的类的类对象类对象:Class类的实例

  • JVM内部每个被加载的类都有且只有一个Class的实例与之对应。
  • JVM加载一个类时会读取该类的.class文件然后将其载入到JVM内部
  • 与此同时会实例化一个Class的实例,用该实例记录被加载的类的信息(类名,方法,构造器等)

获取一个类的类对象方式

  1. 类名.class

    Class cls = String.class;//获取String的类对象
    Class cls = int.class;//获取int的类对象(基本类型只有这一种方式获取类对象)
    
  2. Class.forName(String className)

    //根据类的完全限定(包名.类名)名加载并获取该类的类对象
    Class cls = Class.forName("java.lang.String");
    
  3. ClassLoader类加载器方式

反射对象

  • Class,它的每一个实例用于表示一个类的信息
  • Package,它的每一个实例用于表示一个包的信息
  • Method,它的每一个实例用于表示一个方法
  • Constructor,它的每一个实例用于表示一个构造器
  • Filed,它的每一个实例用于表示一个属性
public static void main(String[] args) throws ClassNotFoundException {Scanner scanner = new Scanner(System.in);System.out.println("请输入一个类名:");String className = scanner.nextLine();Class cls = Class.forName(className);//获取类对象所表示的类的完全限定名String name = cls.getName();System.out.println(name);//仅获取类名name = cls.getSimpleName();System.out.println(name);String packageName = cls.getPackage().getName();System.out.println("包名:"+packageName);//通过类对象获取其表示的类中的所有公开方法(包含从超类继承的方法)Method[] methods = cls.getMethods();for(Method method : methods){//每个Method对象也提供了许多get方法可以获取其表示的方法的相关信息System.out.println(method.getName());//输出方法名}
}

反射机制实例化对象

  1. 获取待实例化类的类对象
  2. 通过类对象的newInstance()方法实例化,该方法会使用其表示的类的公开的,无参构造器进行实例化
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {Person person = new Person();System.out.println(person);/*反射机制实例化1:获取待实例化类的类对象2:通过类对象的newInstance()方法实例化*///        Class cls = Class.forName("reflect.Person");/*java.util.Date (日期类,new一个实例表示实例化是的系统时间)reflect.Personreflect.Studentjava.util.HashMap*/Scanner scanner = new Scanner(System.in);System.out.println("请输入一个类名:");String className = scanner.nextLine();Class cls = Class.forName(className);/*Class提供了可以实例化其表示的类的实例的方法:Object newInstance()该方法会使用其表示的类的公开的,无参构造器进行实例化*/Object obj = cls.newInstance();System.out.println(obj);
}

操作特定构造器进行对象实例化

public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");//Constructor construtor = cls.getConstructor();//获取无参构造器//construtor.newInstance();//Person(String name)Constructor constructor = cls.getConstructor(String.class);Object obj = constructor.newInstance("李四");//new Person("李四");System.out.println(obj);//Person(String name,int age)Constructor constructor2 = cls.getConstructor(String.class,int.class);Object obj2 = constructor2.newInstance("王五",33);//new Person("王五",33);System.out.println(obj2);
}

使用反射机制调用方法

public static void main(String[] args) throws Exception {Person p = new Person();p.playGame();Scanner scanner = new Scanner(System.in);System.out.println("请输入类名:");String className = scanner.nextLine();System.out.println("请输入方法名:");String methodName = scanner.nextLine();//实例化Class cls = Class.forName(className);Object obj = cls.newInstance();/*无参方法*///调用方法Method method = cls.getMethod(methodName);//执行当前Method对象表示的方法method.invoke(obj);//p.playGame();/*有参方法*///getMethod()第一个参数为方法名,从第二个参数开始为该方法的参数列表Method m1 = cls.getMethod("say",String.class);//say(String info)//invoke()第一个参数为方法所属对象,从第二个参数开始就是调用该方法时需要出入的实参m1.invoke(obj,"大家好!");//p.say("大家好!");//say(String info,int count)Method m2 = cls.getMethod("say",String.class,int.class);m2.invoke(obj,"嘿嘿嘿",5);//p.say("嘿嘿嘿",5);}

通过反射机制访问类的私有成员

  • getMethod()getMethods() :仅能获取该类对象所表示的类的 公开方法(包含从超类继承的)
  • getDeclaredMethod() :用来获取Class表示的类的自己定义的方法(包含私有方法)
  • 访问私有方法暴力打开访问权限,使用setAccessible()方法,操作完记得关闭,保证封装性
public static void main(String[] args) throws Exception {//        Person p = new Person();//        p.heihei();//编译不通过!类的外部不能访问类的私有成员Class cls = Class.forName("reflect.Person");Object obj = cls.newInstance();/*java.lang.NoSuchMethodException没有这个方法异常原因:Class的getMethod()和getMethods()仅能获取该类对象所表示的类的公开方法(包含从超类继承的)*///        Method method = cls.getMethod("heihei");//        method.invoke(obj);// p.heihei();/*Class提供了一组:getDeclaredXXXX()都是用于获取本类定义的内容getDeclaredMethod用来获取Class表示的类的自己定义的方法(包含私有方法)*/Method method = cls.getDeclaredMethod("heihei");method.setAccessible(true);//打开该方法的访问权限method.invoke(obj);// p.heihei();method.setAccessible(false);//操作后记得关闭,尽量保证封装性
}

反射机制中访问注解

注解

注解是JDK5之后推出的特性,可以辅助我们在反射中做更多操作,注解被大量的应用于框架中

元注解:JAVA定义了许多元注解,用于说明我们定义的注解的一些特性

  • @Target:用于指定当前注解可以被应用于哪里,不指定时,注解可以被应用于任何可被使用的地方。可以使用ElementType来指定注解可以被应用的位置
取值 作用范围
ElementType.TYPE 注解在类上可以被使用
ElementType.METHOD 注解可以在方法上被使用
ElementType.FIELD 注解可以在属性上被使用
ElementType.CONSTRUCTOR 注解可以在构造器上被使用
ElementType.PACKAGE 注解在包上被使用
ElementType.LOCAL_VARIABLE 注解在局部变量上被使用
ElementType.PARAMETER 注解在参数上被使用
  • @Retention:用来说明当前注解的保留级别,级别有三种。当不指定@Retention时,注解默认的保留级别为CLASS
取值 保留范围
RetentionPolicy.SOURCE 注解仅保留在源代码中
RetentionPolicy.CLASS 注解保留在字节码文件中但不能被反射机制使用
RetentionPolicy.RUNTIME 注解保留在字节码文件中且可以被反射机制使用

注解可以定义参数

  • 格式:类型 参数名() [default 默认值] 注:[] 中的内容可有可无

参数如果指定了默认值,那么使用该注解时可以不指定参数。如果没有指定默认值,则使用该注解时必须指定参数

如果注解中只声明了一个参数时,参数名推荐使用"value"

  • 原因:正常情况下,当我们在注解中声明一个参数时,外面使用该注解为该参数赋值时,格式:@注解名(参数名=参数值)

例:
当前注解@AutoRunMethod中定义了一个int行的参数,名字为count:

public @interface AutoRunMethod {int count() default 1;
}

那么外面在使用该注解时,格式如下

@AutoRunMethod(count=3)
public void sayHi(){....}

如果注解定义了多个参数,比如:

public @interface AutoRunMethod {int count() default 1;String name();
}

外界使用时,为参数赋值的格式

@AutoRunMethod(count=3,name="张三")
public void sayHi(){....}
//-------------或者-------------
@AutoRunMethod(name="张三",count=3)
public void sayHi(){....}
//-------------或者-------------
@AutoRunMethod(name="张三")   //因为count指定了默认值
public void sayHi(){....}

多个参数时,上述写法优点:可读性强 缺点:啰嗦
但是对于只有一个参数的情况下:

@AutoRunMethod(count=1) //对于一个参数时,不存在混淆问题,之带来啰嗦
@AutoRunMethod(1)       //优雅~

为了解决该问题,java建议当注解只有一个参数时,参数名取名为value,此时允许不指定参数名:@AutoRunMethod(1)

对于多参数时,就算某个参数取名为value,使用该直接为参数赋值时也不能省略参数名:

public @interface AutoRunMethod {int value();String name();
}//@AutoRunMethod(value=1,name="XXXX")   可以
// @AutoRunMethod(name="XXXX",value=1)   可以
// @AutoRunMethod(name="XXXX",1)       不可以
//  @AutoRunMethod(1,name="XXXX")       不可以

判断一个类是否被某个注解标注

public static void main(String[] args) throws ClassNotFoundException {//需求:判断Person类是否被注解@AutoRunClass标注Class cls = Class.forName("reflect.Person");/*【所有反射对象】都提供了用于判断是否被某个注解标注的方法boolean isAnnotationPresent(Class cls)判断当前反射对象表示的内容是否被参数类对象表示的注解标注了*///判断cls表示的类Person是否被参数类对象表示的注解AutoRunClass标注了?boolean mark = cls.isAnnotationPresent(AutoRunClass.class);System.out.println("Person是否被@AutoRunClass标注了:"+mark);
}

判断一个方法是否被指定注解标注

public static void main(String[] args) throws Exception {//需求:查看Person类的sayHi方法是否被注解@AutoRunMethod标注了Class cls = Class.forName("reflect.Person");Method method = cls.getDeclaredMethod("sayHi");boolean mark = method.isAnnotationPresent(AutoRunMethod.class);System.out.println(method.getName()+"()是否被注解@AutoRunMethod标注了:"+mark);}

访问注解参数

public static void main(String[] args) throws Exception{//需求:获取Person类中sayHi方法的注解@AutoRunMethod中的参数Class cls = Class.forName("reflect.Person");Method method = cls.getMethod("sayHi");//确定该方法被指定注解标注了if(method.isAnnotationPresent(AutoRunMethod.class)) {//所有的反射对象都支持获取注解的方法: getAnnotation(Class cls)AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);int value = arm.value();//获取注解中"value"参数的值System.out.println(value);}
}