Java基础知识(二)

反射机制

Java中处理基本数据类型,如intchar等,其他均为引用类型。

每个引用类型也是Java中的一个对象,称为类对象,用以记录该类的信息:包括类名、包名、父类、实现的接口、所有方法、字段等。 通过该引用类型创建的实例,称为类的实例对象。

Java对类对象的加载是动态的,只有当JVM第一读取到该类Class(包括Interface)的信息时,才会将该类对象加载到内存中,并将该Class的名称与类对象进行关联。

反射:通过类型的名称,构建类对象,获取该类的信息。

public class ReflectTest {
    public static void main(String[] args) {
        String str = "hello";
        ///获取类对象
        ///1
        Class cls = str.getClass();
        ///2
        cls = String.class;
        ///3
        try {
            cls = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String simpleName = cls.getSimpleName();
        String packageName = cls.getPackageName();
        String name = cls.getName();
        System.out.println(simpleName);//String
        System.out.println(packageName);///java.lang
        System.out.println(name);//java.lang.String
        System.out.println(cls);///class java.lang.String
    }
}
  • 反射调用示例:
///将要反射的类
package learn.cls;
public class Person extends  Object {
    public int age;
    String name;

    public Person(String name) {
        this.name = name;
    }

    public void introduce(String from) {
        System.out.printf("我叫%s,今年%d岁,来自%s%n",name,age,from);
    }
    public String getInfo() {
        return name + " : " +age + "岁";
    }
}
///反射调用
public class ReflectTest {
    public static void main(String[] args) {
        ///反射创建实例和调用方法
        try {
            Class cls = Class.forName("learn.cls.Person");
            ///调用指定的构造函数
            Object instance = cls.getDeclaredConstructor(String.class).newInstance("张三");
            ///反射字段
            Field field = cls.getDeclaredField("age");
            field.set(instance,18);
            ///获取该字段的值
            Integer age = field.getInt(instance);
            System.out.println(age);///18
            ///反射方法
            Method method = cls.getDeclaredMethod("introduce", String.class);
            ///方法调用
            method.invoke(instance,"上海");///我叫张三,今年18岁,来自上海
            ///反射调用获取返回值
            Method method1 = cls.getDeclaredMethod("getInfo");
            String info = (String) method1.invoke(instance);
            System.out.println(info);///张三 : 18岁
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
///上述反射调用等同于下述代码
Person person = new Person("张三");
person.age = 18;
Integer age = person.age;
System.out.println(age);
person.introduce("上海");
String info = person.getInfo();
System.out.println(info);

动态代理

动态代理:JDK提供的动态创建接口对象的方式。定义了接口,但是并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。

  • 示例1:
///接口
interface Game {
    void play(String toy);
    String category();
}

public class DynamicDelegateTest {
    public static void main(String[] args) {
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               ///等同接口方法的实现
                if (method.getName().equals("play")) {
                    String out = "let us play " + args[0];
                    System.out.println(out);
                } else if (method.getName().equals("category")) {
                    return "游戏";
                }
                return null;
            }
        };
       ///不写实现类,直接创建接口对象
        Game game = (Game) Proxy.newProxyInstance(Game.class.getClassLoader(), new Class[]{Game.class},invocationHandler);
        game.play("basketball");///let us play basketball
        String str = game.category();//游戏
        System.out.println(str);
    }
}
///上述代码的本质
public class DynamicDelegateTest implements Game {
    public InvocationHandler invocationHandler;
    public  DynamicDelegateTest(InvocationHandler handler){
        this.invocationHandler = handler;
    }
    @Override
    public void play(String toy) {
        try {
            this.invocationHandler.invoke(this, getClass().getDeclaredMethod("play", String.class), new Object[]{toy});
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    @Override
    public String category() {
        String result = null;
        try {
           result = (String)this.invocationHandler.invoke(this, getClass().getDeclaredMethod("category"), null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return  result;
    }
}

示例2:动态代理实现接口方法的监听

public class DynamicDelegateTest {
    
    public static void main(String[] args) {
        Game game = new Game() {
            @Override
            public void play(String toy) {
                System.out.println("玩游戏中...");
            }

            @Override
            public String category() {
                return "游戏";
            }
        };
        ///game:最终执行方法的对象 proxyGame:实现接口方法监听生效的对象
        Game proxyGame = (Game) proxyObject(game);
        ///启用接口方法监听
        proxyGame.play("篮球");
        ///输出:
        /*
        方法:play,调用前
        玩游戏中...
        方法:play,调用后
        */
    }

    public static Object proxyObject(Game target) {
        Class[] interfaces = {Game.class};
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.printf("方法:%s,调用前%n", method.getName());
                ///对接口方法进行监听后,由被代理的对象去执行
                Object objc = method.invoke(target, args);
                System.out.printf("方法:%s,调用后%n", method.getName());
                return objc;
            }
        };
        return Proxy.newProxyInstance(Game.class.getClassLoader(), interfaces, handler);
    }
}

Java注解

注解的概念

Java中的注解是一种可以放在类、字段、方法、参数前的特殊”注释“。
Java中的注释会被编译器忽略,注解则可以被编译器打包进.class文件,因此注解是一种用作标注的元数据。

Java中使用@interface来定义注解。注解的参数类似无参方法。声明注解参数时,推荐使用default关键字为其设定默认值;常用的注解参数命名为value,可在注解参数只有一个时,省略其参数名称。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Range {
    int min() default 0;
    int max() default 100;
    int value() default 50;
}

元注解

可以修饰其他注解的注解称为元注解。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。

  1. @Target:定义注解可以被用于源码的哪些位置。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    //注解参数为数组,可以定义多个位置
    ElementType[] value();
}
///该注解的参数类型:`ElementType`
public enum ElementType {
    TYPE,///类、接口、标注接口,枚举、record 声明
    FIELD,///类的字段声明、包括枚举常量
    METHOD,///方法声明
    PARAMETER,///形式参数声明
    CONSTRUCTOR,///构造函数声明
    LOCAL_VARIABLE,///本地变量声明
    ANNOTATION_TYPE, ///注解类型声明
    PACKAGE,///Package声明
    TYPE_PARAMETER,///类型参数声明
    TYPE_USE,///使用类型
    MODULE,///模块声明
    RECORD_COMPONENT;///record部分
}

2.@Retention: 定义注解的生命周期
如果在一个注释接口声明中没有Retention注释,保留策略默认为RetentionPolicy.CLASS

/*If no Retention annotation is present on an annotation interface declaration, the retention policy defaults to RetentionPolicy.CLASS.*/
public @interface Retention {
    RetentionPolicy value();
}
///该注解的参数类型:`RetentionPolicy `
public enum RetentionPolicy {
    ///Annotations are to be discarded by the compiler.
    ///修饰的注解被编译器舍弃
    SOURCE, ///编译器
    /// Annotations are to be recorded in the class file by the compiler
    ///  but need not be retained by the VM at run time
   ///修饰的注解将被编译器记录到`java`类的`.class`文件中,但在运行时不需要被`JVM`保留
    CLASS,///class文件

    /// Annotations are to be recorded in the class file by the compiler and
    /// retained by the VM at run time, so they may be read reflectively.
    /// 修饰的注解将被编译器记录到`java`类的`.class`文件中,并且运行时会被`JVM`保留
    RUNTIME///运行期
}
  1. @Repeatable:注释其声明的注释接口是可重复的。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}
///可重复使用注解的定义
@Repeatable(value = Ranges.class)
@Target(ElementType.TYPE)
public @interface Range {
    int min() default 0;
    int max() default 100;
    int value() default 50;
}

@Target(ElementType.TYPE)
@interface Ranges {
    Range[] value();
}
///具体使用
@Range(min = 10,max = 200,value = 120)
@Range(80)
@Range(value = 19)
class Test {
}
  1. @ Inherited
    使用@Inherited定义子类是否可继承父类定义的注解。@Inherited仅针对@Target(ElementType.TYPE)类型的注解有效,并且仅针对class的继承,对interface的继承无效:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
///示例
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}
///父类使用了这个被`@Inherited`修饰的注解
@Report(type=1)
public class Person {
}
///子类默认也继承了该父类的注解
public class Student extends Person {
}

注解的定义

第一步:使用@interface定义注解

public @interface Report {
}

第二步:定义注解的参数、默认值

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

把最常用的参数定义为value(),推荐所有参数都尽量设置默认值。
第三步:用元注解配置注解

@Target(ElementType.TYPE)///注解类、接口、枚举等
@Retention(RetentionPolicy.RUNTIME)///运行期保留注解
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

其中,必须设置@Target@Retention@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期读取。一般情况下,不必写@Inherited@Repeatable

注解的处理

注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API

  • Java中提供的与注解相关的反射API

///-------判断注解是否存在------

///判断类是否有注解
AnnotationTest.class.isAnnotationPresent(Range.class);
///判断类的构造函数是否有注解
AnnotationTest.class.getDeclaredConstructor().isAnnotationPresent(Range.class);
///判断某个方法是否有注解
Method method = AnnotationTest.class.getMethod("getSomeUsefulInfo");
method.isAnnotationPresent(Range.class);
///判断某个字段是否有注解
Field field = AnnotationTest.class.getField("userType");
field.isAnnotationPresent(Range.class);

///-------读取注解信息------

///获取类的注解
AnnotationTest.class.getAnnotation(Range.class);
try {
    ///获取类的构造函数的注解
    AnnotationTest.class.getDeclaredConstructor().getAnnotation(Range.class);
    ///获取某个方法的注解
    AnnotationTest.class.getMethod("getSomeUsefulInfo").getAnnotation(Range.class);
    //获取某个字段的注解
    Field field = AnnotationTest.class.getField("userType");
    field.getAnnotation(Range.class);
 } catch (NoSuchMethodException e) {
      e.printStackTrace();
 } catch (NoSuchFieldException e) {
      e.printStackTrace();
}
  • Java注解的示例
    1.定义两个注解:RouterGetFuncInfo,分别用来注解类和方法
///注解类,定义类的实例对象需要执行的操作
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Router {
    String value() default "";
    String operation() default "getInfo";
}

///注解方法,并设置方法调用的默认参数
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetFuncInfo {
    String value() default "";
}
  1. 注解的类和方法
@Router(operation = "getSomeUsefulInfo",value = "markedClass")
public class AnnotationTest {
    
    public String param0;
    public String param1;
    public int param2;
    
    @GetFuncInfo("张三的个人信息")
    public void getSomeUsefulInfo(String param0){
        System.out.printf("getSomeUsefulInfo:%s",param0);
    }
    
    public void functionWithSomeParameters(String p1, int p2) {
        System.out.println(p1);
        System.out.println(p2);
    }
}

3.处理注解

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        ///定义操作
        String operate = "getSomeUsefulInfo";
        ///扫描`Router`注解的类,此处假设已知
        String classStr = "annotation.AnnotationTest";
        Class cls = Class.forName(classStr);

        if (cls.isAnnotationPresent(Router.class)){
            ///获取类的注解
            Router router = (Router)cls.getAnnotation(Router.class);
            System.out.println(router);///@annotation.Router(value="markedClass", operation="getSomeUsefulInfo")
            ///根据注解的操作,找到对应的方法
            String operateStr = router.operation();
            for (Method method: cls.getMethods()) {
                if (method.getName().equals(operateStr)) {
                    ///获取方法的注解
                    if (method.isAnnotationPresent(GetFuncInfo.class)){
                        GetFuncInfo funcInfo = method.getAnnotation(GetFuncInfo.class);
                        System.out.println(funcInfo);///@annotation.GetFuncInfo("\u5f20\u4e09\u7684\u4e2a\u4eba\u4fe1\u606f")
                        ///读取注解参数
                        String param = funcInfo.value();
                        ///创建对象实例
                        Object clsObj = cls.getDeclaredConstructor().newInstance();
                        ///调用方法,传入注解参数
                        method.invoke(clsObj,param); ///getSomeUsefulInfo:张三的个人信息
                    }
                }
            }
        }

    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容