关于注解和反射
关于注解和反射
本博客中的所有内容都是来自b站狂神说视频教程,如有侵权联系我删除。
下面是视频链接:b站狂神说
什么是注解
annotation,注解,Java通过注解来让编译器读取,注解不影响代码
Java内置注解

Java四大元注解

@Target:解释其他注解的注解,表示注解可以用到哪些地方
@Retention: 表示什么时候这个注解有用,一般是RUNTIME运行时。
source:源码时有效。
class:编译后有效。
runtime:运行时有效。
source < class < runtime
代码示例
/**
* 元注解
*
* @Date 16:14 2020/8/5
*/
public class Test01 {
@mmyAnnotation
public void Test1(){
}
}
//定义一个注解:
/**
* 有了type之后可以把注解放到类上,而不是方法上
*/
@Target(value = {ElementType.METHOD,ElementType.TYPE})
/**
* Retention表示注解在什么时候有效
*/
@Retention(value = RetentionPolicy.RUNTIME)
//表示是否把注解生成在javadoc中。
@Documented
//子类是否可以继承父类的注解
@Inherited
@interface mmyAnnotation{
}
自定义注解
代码
通过一段代码来分析:
/**
* 自定义注解
*
* @Date 15:59 2020/8/6
*/
public class Test02 {
//注解可以显示赋值,如果没有赋值,就必须给注解默认值
@myannotation2(name = "abc")
public void test(){
}
@myAnnotation3("avc")
public void test2(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface myannotation2{
//定义注解的参数 参数类型+ 参数名 ();
//default添加默认值
String name() default "";
int age() default 12;
int id() default -1;//如果默认值为-1,就表示不存在。
String[] schools() default {"1,2,3"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation3{
String value();
}
注解的定义方式:参数类型+ 参数名 ();
注解中只有value属性可以在注解使用时的括号内直接赋值,其他的不可以。
一个比较重要的:如果注解使用时不符值,就必须要提前有默认值。
反射机制
运用反射会影响效率,所以能不用就不用。
什么是反射

Reflection(反射),动态语言的关键,借助Reflection API取得任何类的内部信息,直接操作任何对象的内部属性和方法。(就牛逼的不行!)

hashcode相同,他们都指向同一个类。
getClass()方法是object的一个方法,Class本身也是一个类,由于所有类都继承object类,所以可以通过对象来找到类。
Class类的常用方法:

Class类的创建方式:
package com.grss.reflection;
/**
* 测试class类的创建方式
*
* @Date 17:32 2020/8/6
*/
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:"+person.name);
//方式一,通过对象获得
Class<? extends Person> c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二,通过forname获取
Class<?> c2 = Class.forName("com.grss.reflection.Student");
System.out.println(c2.hashCode());
//方式三,通过类名来获得
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());
//方式四,基本内置类型的包装类都有一个Type属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
//获得一个类的父类
Class<?> c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name="学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name="老师";
}
}
效果图:

上面介绍了三种常规的获得类对象的方法,和一种只有内置类的包装类的方式获得类对象的方法。
.getSuperclass():获得一个类的父类。
所有类的Class对象
package com.grss.reflection;
import java.lang.annotation.ElementType;
/**
* 所有类的Class
*
* @Date 18:00 2020/8/6
*/
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举类型
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型和维度一样,就都指向同一个类
int[] a= new int[10];
int[] b= new int[100];
int[][] d=new int[0][10];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
System.out.println(d.getClass().hashCode());
}
}
效果图:

前几个是各种类的Class对象
后三个是一个测试,一维数组和二维数组,只要维度相同,class对象都相同,维度不同,class对象不同。
java类加载内存分析
package com.grss.reflection;
/**
* 类加载内存分析
*
* @Date 10:21 2020/8/7
*/
public class Testt04 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
/**
* 1,加载到内存会产生一个类的class对象
* 2,链接,链接结束后m=0
* 3,初始化(new)
* <clinit>(){
* System.out.println("A类静态代码块初始化");
* m=300;
* m=100;
* }
* 类的静态的代码块会按顺序执行,上面先赋值300,下面再赋值100
*/
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m=300;
}
static int m=100;
public A() {
System.out.println("A类的无参构造器加载");
}
}
效果图:

类内存分析图:

类的内存加载,先在方法区中,加载时先在堆中生成类的Class对象,Class对象中有类的所有数据。初始化时(new时)就会在栈中把静态代码块中的数据合并,这个合并过程是按顺序执行的,上面的代码中,m先是300后是100所以结果为100.若换下位置,结果就是300.
顺便一提,加载时,是先加载静态代码块,后加载无参构造初始化类,然后才是方法。
换位置后的效果图:

什么时候会发生类的初始化
类的主动引用时会发生类的初始化,类被动引用时不会发生类的初始化。
package com.grss.reflection;
/**
* 类在什么时候初始化
*
* @Date 11:10 2020/8/7
*/
public class Test05 {
static {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用
// Son son = new Son();
//反射也会产生主动引用
// Class.forName("com.grss.reflection.Son");
//非主动引用
// System.out.println(Son.b);
// Son[] array=new Son[5];
System.out.println(Son.n);
}
}
class Fathsr{
static int b=2;
static {
System.out.println("父类被加载");
}
}
class Son extends Fathsr{
static {
System.out.println("子类被加载");
m=300;
}
static int m =100;
static final int n=1;
}
类只要被加载,main类就会被加载。
主动引用时,子类被加载,父类也会被加载:

别动引用时,用子类调父类方法,子类不会被加载。

创造一个与定义的类无关的数组时,只有main类被加载:

用子类调一个静态常量,子类和父类都不会被加载,只有main类被加载:

通过反射创建对象
先通过前面说过的三种方式来获得class对象,通过class对象来获得对象。
package com.grss.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射创造对象
*
* @Date 14:32 2020/8/9
*/
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.grss.reflection.User");
//调用无参构造器来创建对象
User user = (User) c1.newInstance();
//通过构造器创建对象
User user1 = (User) c1.getConstructor(String.class, int.class, int.class).newInstance("雷怡",1,18);
//通过反射获得普通方法
User user2 = (User) c1.newInstance();
//通过反射获得一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//调用上面获取到的方法。
//invoke:激活
//invoke(对象,“方法的值”)
setName.invoke(user2, "怡宝");
User user3 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//关闭权限开关
name.setAccessible(true);
name.set(user3,"怡宝");
System.out.println(user3.getName());
}
}
获得对象主要通过无参构造和有参构造两种。
获得方法需要用到getDeclaredMethod(),参数一填方法名,参数2:数据类型、
想要获得一个对象的私有属性的时候,要记得关闭权限开关:setAccessible(true);
getConstructor/newInstance:通过构造获得类。
getDeclaredMethod:获得方法
getDeclaredField:获得属性
setAccessible(true):关闭权限开关,调用私有(private)时需要用到。
关于反射的性能分析
package com.grss.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 性能分析
*
* @Date 17:37 2020/8/10
*/
public class Test08 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
test1();
}
//普通
public static void test1(){
User user = new User();
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime= System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)+"ms");
}
//反射创建对象的
public static void test2() throws IllegalAccessException, InstantiationException {
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
User user = User.class.newInstance();
user.getName();
}
long endTime= System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)+"ms");
}
//反射获取方法的
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
long startTime=System.currentTimeMillis();
Method getName = User.class.getDeclaredMethod("getName");
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime= System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)+"ms");
}
//获得属性
public static void test4() throws NoSuchFieldException {
long startTime=System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
Field name = User.class.getField("name");
name.setAccessible(true);
}
long endTime= System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)+"ms");
}
//反射,关闭权限检测后
public static void test5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
long startTime=System.currentTimeMillis();
Method getName = User.class.getDeclaredMethod("getName");
getName.setAccessible(true);
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime= System.currentTimeMillis();
System.out.println("用时:"+(endTime-startTime)+"ms");
}
}
上面对比了用反射来创建对象,和用new来创建对象用时区别。new完爆反射。
直接调用getName方法和用反射,关闭检测的反射。
直接最快,关闭权限比不关要快三倍左右。
浙公网安备 33010602011771号