IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java知识点06——注解(Annotation)、类加载机制、反射 -> 正文阅读

[Java知识库]Java知识点06——注解(Annotation)、类加载机制、反射

声明:

  1. 该资料来自自己整理。
  2. 参考书籍 疯狂 Java讲义(第五版) 李刚?著

一、注解

1.1 标记注解(Annotation)

@Override

限定重写父类方法:@Override

class Fruit{
    public void info(){
        System.out.println("Fruit的info方法...");
    }
}

public class AnnotationTest extends Fruit{
    //使用@Override指定下面方法必须重写父类方法
    @Override
    public void info() {
        System.out.println("AnnotationTest重写Fruit的info方法...");
    }
}

注意:@Override只能修饰方法,不能修饰其它程序元素

@Deprecated

@Deprecated 用于标识某个程序元素(类,方法等)已过时,当其它程序使用已过时的类、方法时,编译期将会给出警告。

@Deprecated注解增加了如下两个属性:(JDK9 新增的)

  • forRemoval:该boolean类型的属性指定该API在将来是否会被删除。
  • since:该String类型的属性指定该API从哪个版本被标记为过时。
class Fruit{
    //定义info方法已过时
    //since属性指定从哪个版本开始,forRemoval指定该API将来会被删除
    //@Deprecated(since="9",forRemoval=true)
    @Deprecated
    public void info2(){
        System.out.println("即将过时的方法...");
    }
}

public class AnnotationTest extends Fruit{
    public static void main(String[] args) {
        //使用@Deprecated方法时将会被编译器警告
        new Fruit().info2();
    }
}


@SuppressWarnings

抑制编译器警告:@SuppressWarning

@SuppressWarning指示被该注解修饰的程序元素(以及该程序元素中的所有子元素),取消显式指定的编译器警告。@SuppressWarning会一直作用于该程序元素的所有子元素。只能保存在源码级别上。

//关闭整个类里的编译器警告
@SuppressWarnings(value = "unchecked")
public class SuppressWarningTest {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
    }
}

使用@SuppressWarning注解来关闭编译器警告时,一定要在括号里使用 name=value 的形式为该注解的成员变量设置值。

@SafeVarargs

”堆污染“警告与@SafeVarargs。

当把一个不带泛型的独享赋给一个带反省的变量时,往往会发生”堆污染“。例如:

List<String> listStrArray = new List<String>();
List[] listArray = listStrArray;//把List<String>赋给List,发生堆污染。!!!

抑制该堆污染警告有如下三种方式:

  • 使用@SafeVarargs修饰修饰引发该警告的方法或构造器。JDK 9允许使用该注释修饰私有实例方法。
  • 使用@SuppressWarnings("unchecked")修饰。
  • 编译时使用 -Xlint:varargs选项。

@FunctionalInterface

@FunctionalInterface就是用来指定某个接口必须是函数式接口。

//@FunctionalInterface只是告诉编译器检查这个接口,
// 保证该接口只能包含一个抽象方法,否则就会编译出错。
@java.lang.FunctionalInterface
public interface FunctionalInterface {
    void test();
}

@FunctionalInterface只能修饰接口,不能修饰其他程序元素。

1.2 元注解(meta-annotation)

@Retention【重点】

@Retention:表示需要在什么级别保存该注释信息,用于描述注解的声明周期。

SOURCE < CLASS < RUNTIME



@Target 【重点】

@Target用于描述注解的使用范围(即:被描述的注解可用在什么地方)

@Document

@Document说明该注释将被包含在javadoc中

@Inherited

@Inherited说明子类可以继承父类中的该注释

1.3 自定义注解

@interface 关键字

//定义一个简单的注解类型
public @interface Test(){
    //注解的参数格式:参数类型 + 参数名();
}

//使用@Test修饰类定义
@Test
public class MyClass{
    //使用@Test注解修饰方法
    @Test
    public void info(){···}
}

1.4 AnnotatedElement 接口

实现类:

1.4.1 提取注解信息

java.lang.reflect 包所提供的反射 API 增加了读取运行时注解的能力。

只有当定义注解时使用了@Rentention(RetentionPolicy.RUNTIME) 修饰,该注解才会在运行时可见,JVM才会在装载 *.class 文件时读取保存在 class 文件中的注解信息。

二、类加载机制

2.1 JVM 和 类

  • 当Java程序运行结束时,JVM进程结束,该进程在内存中的状态将会丢失。

  • 两个 JVM 之间并不会共享数据

例如【注意细节】:

public class A {
    //定义该类的类变量
    public static int a = 6;
}
public class ATest {
    public static void main(String[] args) {
        A a = new A();
        a.a ++;//推荐使用 类名.静态变量 来调用static修饰的静态变量
        System.out.println(a.a);//7
    }
}
public class ATest2 {
    public static void main(String[] args) {
        A b = new A();
        System.out.println(b.a);//6
        //第一次运行JVM结束后,它对A类所做的修改将全部丢失
        //第二次运行JVM时将再次初始化A类,所以输出初始值6
    }
}

2.2 类的加载(类初始化)步骤

2.2.1 类的加载(步骤一)

一个类在内存中只有一个Class对象;一个类被加载后,类的整个结构都会被封装在Class对象中。

2.2.2 类的连接(步骤二)

2.2.3 类的初始化(步骤三)

当程序主动使用任何一个类时,系统会保证该类以及所有父类(包括直接父类和间接父类)都会先被初始化。

2.3 类初始化的时机


注意:一

class MyTest{
    static{
        System.out.println("静态初始化块...");
    }
    //使用一个字符串直接量为static final的类变量赋值
    static final String compileConstant = "go go go";
}

public class CompileConstantTest {
    public static void main(String[] args) {
        //下列代码不会导致初始化MyTest类
        System.out.println(MyTest.compileConstant);
    }
}





注意:二

2.4 类加载器




【案例】

public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类的加载器:"+systemClassLoader);

        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println("扩展类加载器:"+parent);

        //获取扩展类加载器的父类加载器-->根加载器(C/C++)
        //根加载器Java不能直接获得
        ClassLoader parentRoot = parent.getParent();
        System.out.println("根加载器(C/C++):"+parentRoot);

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("JVMAndClass.ClassLoaderTest").getClassLoader();
        System.out.println("当前类的加载器:"+classLoader);

        //测试JDK内置的类是谁加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println("JDK内置的加载器是谁加载的:"+classLoader);

        //系统类加载器的加载路径
        System.out.println(System.getProperty("java.class.path"));
    }
}

三、反射

3.1 简介


获取对象的方式

3.2 优缺点

优点可以实现动态创建对象和编译,体现出很强的灵活性

缺点对性能有影响

3.3 主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

3.4 获取Class类的实例


【案例】

public class Reflaction01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();
        System.out.println("这个人是:"+student.name);//这个人是:学生

        //方式一:通过对象获得 Class 对象
        Class c1 = student.getClass();
        System.out.println(c1.hashCode());//1173230247

        //方式二:通过 forName 获得
        Class c2 = Class.forName("reflaction.Student");
        System.out.println(c2.hashCode());//1173230247

        //方式三:通过 类名.class 获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());//1173230247

        //方式四:基本内置类型的包装类都有一个 Type 属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);//int
        
        //获得父类类型
        Class superclass = c1.getSuperclass();
        System.out.println(superclass);//class reflaction.Person
    }
}

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 = "老师"; }
}

3.5 所有类型的Class

//所有类型的Class
public class AllClass {
    public static void main(String[] args) {
        Class c1 = Object.class;//Object
        Class c2 = Comparable.class;//interface
        Class c3 = String[].class;//String
        Class c4 = int[][].class;
        Class c5 = Override.class;//Annotation注解类型
        Class c6 = ElementType.class;//enum 类型
        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);
        
        //只要元素类型与维度一样,就是同一个 class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());//1173230247
        System.out.println(b.getClass().hashCode());//1173230247
    }
}

3.6 通过反射查看类信息

4.5.1、获得Class对象

4.5.2、从Class中获取信息

返回Class对应类的构造器

返回Class对应类所包含的方法

返回Class对应类所包含的成员变量

返回Class对应类所包含的Annotation

返回Class对应类所包含的内部类、外部类、实现的接口、所继承的父类

返回Class对应类的修饰符、所在包、类名等基本信息

4.5.3、JDK 8新增的方法参数反射

class Test{
    public void replace(String str, List<String> list){}
}
public class MethodParameterTest {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Test> testClass = Test.class;
        //获取Test类的带两个参数的replace()方法
        Method replace = testClass.getMethod("replace", String.class, List.class);
        //获取指定方法的参数个数
        System.out.println("replace方法参数个数:"+replace.getParameterCount());
        //获取replace的所有参数信息
        Parameter[] parameters = replace.getParameters();
        int index = 1;
        //遍历所有参数
        for (Parameter p : parameters){
            if (p.isNamePresent()){
                System.out.println("----第"+ index++ +"个参数信息----");
                System.out.println("参数名:"+p.getName());
                System.out.println("参数类型:"+p.getType());
                System.out.println("泛型类型:"+p.getParameterizedType());
            }
        }
    }
}


4.5.4、练习

//定义可重复注解
@Repeatable(Annos.class)
@interface Anno{}

@Retention(value = RetentionPolicy.RUNTIME)
@interface Annos{
    Anno[] value();
}

//使用4个注解修饰该类
@SuppressWarnings(value = "unchecked")
@Deprecated
//使用重复注解修饰该类
@Anno
@Anno
public class ClassTest {
    //为该类定义一个私有的构造器
    private ClassTest(){}
    //定义一个有参数的构造器
    public ClassTest(String name){
        System.out.println("执行有参数的构造器");
    }
    //定义一个无参数的info方法
    public void info(){
        System.out.println("执行无参数的info方法");
    }
    //定义一个有参数的info方法
    public void info(String str){
        System.out.println("执行有参数的info方法,其参数为:"+str);
    }
    //定义一个测试用的内部类
    class Inner{}
    //main
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
        //获取ClassTest对应的Class
        Class<ClassTest> classTestClass = ClassTest.class;
        System.out.println("ClassTest的带一个参数的构造器如下");
        System.out.println("一个参数构造器-->"+classTestClass.getConstructor(String.class));
        
        //获取该Class对象所对应类的public构造器
        Constructor<?>[] constructors = classTestClass.getConstructors();
        System.out.println("ClassTest的public构造器如下");
        for (Constructor constructor : constructors){
            System.out.println("constructor-->"+constructor);
        }
        
        //获取该Class对象所对应类的全部构造器
        Constructor<?>[] declaredConstructors = classTestClass.getDeclaredConstructors();
        System.out.println("ClassTest的全部构造器如下");
        for (Constructor declaredConstructor : declaredConstructors){
            System.out.println("declaredConstructor-->"+declaredConstructor);
        }
        System.out.println("ClassTest的带一个参数的方法如下");
        System.out.println("一个参数方法-->"+classTestClass.getMethod("info", String.class));
        
        //获取该Class对象所对应类的public方法
        Method[] methods = classTestClass.getMethods();
        System.out.println("ClassTest的public方法如下");
        for (Method method : methods){
            System.out.println("method-->"+method);
        }
        
        //获取该Class对象所对应类的全部方法
        Method[] declaredMethods = classTestClass.getDeclaredMethods();
        System.out.println("ClassTest的public方法如下");
        for (Method declaredMethod : declaredMethods){
            System.out.println("declaredMethod-->"+declaredMethod);
        }

        //获取该Class对象对应类的全部注解
        System.out.println("该Class对象对应类的全部注解如下");
        Annotation[] annotations = classTestClass.getAnnotations();
        for (Annotation annotation : annotations){
            System.out.println("annotation-->"+annotation);
        }
        System.out.println("该Class元素上的@SuppressWarnings注解为-->"+classTestClass.getAnnotationsByType(SuppressWarnings.class));

        //获取该Class对象所对应类的全部内部类
        Class<?>[] declaredClasses = classTestClass.getDeclaredClasses();
        System.out.println("ClassTest的全部内部类如下:");
        for (Class declaredClass : declaredClasses){
            System.out.println("declaredClass-->"+declaredClass);
        }

        //使用Class.forName()方法加载ClassTest的内部类
        Class<?> innerClass = Class.forName("reflaction.ClassTest$Inner");
        System.out.println("innerClass-->"+innerClass);
        
        //通过getDeclaringClass()访问该类所在的外部类
        System.out.println("Inner内部类所在的外部类-->"+innerClass.getDeclaringClass());
        System.out.println("ClassTest的包为-->"+classTestClass.getPackage());
        System.out.println("ClassTest的父类为-->"+classTestClass.getSuperclass());
    }
}




3.7 动态创建对象执行方法



setAccessible()方法

public class CreateInstanceUseMethod {
    public static void main(String[] args) throws Exception {
        //获得Class对象
        Class<?> c1 = Class.forName("reflaction.Person");
        
        //构造一个对象
        Person p = (Person) c1.newInstance();//本质上是调用了类的无参构造器,没有无参构造器会报错
        System.out.println(p);//Person{name='null', id=0}
        
        //通过构造器来创建对象
        Constructor<?> constructor = c1.getDeclaredConstructor(String.class);
        Person p2 = (Person) constructor.newInstance("孙悟空");
        System.out.println(p2);//Person{name='孙悟空', id=0}
        
        //通过反射调用普通方法
        Person p3 = (Person) c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);
        setName.invoke(p3,"唐僧");//invoke 激活的意思 invoke(对象,"方法的值");
        System.out.println(p3.getName());//唐僧

        //通过反射操作属性
        System.out.println("----------通过反射操作属性----------");
        Person p4 = (Person) c1.newInstance();
        Field id = c1.getDeclaredField("id");//私有属性直接访问会报错
        
        //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
        id.setAccessible(true);//关闭安全检测即可访问和操作私有属性
        id.set(p4,999);//此时不会报错 java.lang.IllegalAccessException:
        System.out.println(p4.getId());

    }
}

3.8 性能比较

public class PerformanceTest {
    //普通方法创建
    static void test1(){
        Person p = new Person();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            p.getId();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
    }
    //反射方法创建
    static void test2() throws Exception {
        Person p = new Person();
        Class<? extends Person> c1 = p.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(p,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
    }
    //反射方法创建 关闭检测
    static void test3() throws Exception {
        Person p = new Person();
        Class<? extends Person> c1 = p.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(p,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射关闭检测方式执行10亿次:"+(endTime-startTime)+"ms");
    }
    public static void main(String[] args) throws Exception {
        test1();
        test2();
        test3();
    }
}

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:16:56  更:2022-02-26 11:20:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:47:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码