注解

参考B站狂什说教程,需要的小伙伴可以去B站关注下UP主

也是我的学习笔记

一.什么是注解

通俗的来说我们的注解就是解释这个类是用来干嘛的

是用来解释类的,这点我们可以百度到很多关于注解的解释,这点我们就不做太多解释,感兴趣的可以搜索下

最简单的例子

public class Test01 extends Object {@Override           //这里就是重写注解,代表下面是重写了这个函数public String toString() {return super.toString();}
}

在JUC也有@FunctionInterface代表是函数式接口等等等等,都是我们的Java常见的注解

当然有些注解是可以加一些参数的,后面的笔记会涉及到

二.内置注解

上面的@Override就是一个内置注解

常用 的内置注解还有下面的这些

@Deprecated //不推荐程序员使用,但是还可以用,获取有更好的方法
@SuppressWarnings(value = "sdfsaf")//正压警告注解   这个就可以传值哦public @interface SuppressWarnings {String[] value();
}

三.元注解

作用

负责注解其他的注解

常见注解

➢@Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
➢@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
➢(SOURCE < CLASS < RUNTIME)
➢@Document:说明该注解将被包含在javadoc中
➢@Inherited: 说明子类可以继承父类中的该注解

自定义元注解

简单的例子

public class Test01 extends Object {}
//表示我们的注解可以用在哪个地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//表示我们的注解在那个地方有效
@Retention(value = RetentionPolicy.RUNTIME)
//  表示是否将我们的注解生成在Javadoc中
@Documented
//表示子类可以继承父类的注解
@Inherited
@interface MyAnnotation{}

进阶

public class Test01 {//这里我们注解可以显示赋值,如果没有默认值,就必须给注解赋值@MyAnnotation(schools = {"河科大","软件学院"})public void test(){}@MyAnnotation2("sss")//一个参数,我们的名称就可以省略了public void test1(){}}
//表示我们的注解可以用在哪个地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//表示我们的注解在那个地方有效
@Retention(value = RetentionPolicy.RUNTIME)
//  表示是否将我们的注解生成在Javadoc中
@Documented
//表示子类可以继承父类的注解
@Inherited
@interface MyAnnotation{//注解的参数String name() default "";int age() default 0;int id() default -1;//如果默认值为-1,表示不存在String[] schools();
}
//表示我们的注解可以用在哪个地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//表示我们的注解在那个地方有效
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation2{String value();
}

四.反射机制

借用狂神说UP主的资料

静态VS动态语言
动态语言
➢是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被
引进,已有的函数可以被删除或是其他结构.上的变化。通俗点说就是在运行时代
码可以根据某些条件改变自身结构。
➢主要动态语言: Object-C、 C#、 JavaScript、 PHP、 Python等。
静态语言
➢与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、 C、C++。
➢Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一-定的动态性,
我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

什么是反射

Java Reflection
➢Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class C = Class forName(iva lang. String")

举一个简单的例子,我们用反射来操作一个类,但是一般情况下有的类的参数是私有的,反射就可以做到,把他的访问属性给修改了。

➢加载完类之后,在堆内存的方法区中就产生了-个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对
象看到类的结构。这个对象就像一面镜子, 透过这个镜子看到类的结构,所以,我们形象的称之为:反射

反射机制的大概功能

➢在运行时判断任意-个对象所属的类
➢在运行时构造任意一个类的对象
➢在运行时判断任意一个类所具有的成员变量和方法
➢在运行时获取泛型信息
➢在运行时调用任意一个对象的成员变量和方法
➢在运行时处理注解
➢生成动态代理

优缺点

优点:
➢可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
➢对性能有影响。使用反射基本上是- -种解释操作,我们可以告诉JVM,,我们希望
做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

初试反射

public class Demo {public static void main(String[] args) throws ClassNotFoundException {//通过反射获取User的Class对象Class c1 = Class.forName("com.annotation.User");System.out.println(c1);Class c2 = Class.forName("com.annotation.User");Class c3 = Class.forName("com.annotation.User");Class c4 = Class.forName("com.annotation.User");System.out.println(c2.hashCode());System.out.println(c3.hashCode());System.out.println(c4.hashCode());}
}
//实体类 pojo
class User{private String name;private int id;private int age;//此处省略构造,set/get/tostring函数
}

我们发现他们的hashcode值一样,

也就是说一个类在内存中只有一个Class对象

一个类被加载后,类的整个结构都会被封装到Class对象中

Class类

➢Class 本身也是一个类
➢Class对象只能由系统建立对象
➢一个加载的类在JVM中只会有一个Class实例
➢一个Class对象对应的是一-个加载到JVM中的一一个.class文件
➢每个类的实例都会记得自己是由哪个Class实例所生成
➢通过Class可以完整地得到一个类中的所有被加载的结构
➢Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的
Class对象

常用方法

方法名 功能
static ClassforName(String name) 返回指定类名name的Class对象
Object newInstance() 调用确省构造函数,返回Class对象的一个实例
getName() 返回此Class对象所表示的实体(类,接口,数组类或void)的名称。
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassL oader getClassL oader() 返回该类的类加载器
Constructor[ getConstructors() 返回一一个包含某些Constructor对象的数组
Method getMothed(String name,Class… T) 返回一个Method对象,此对象的形参类型为
paramType
Field[] getDeclaredFields() 返回Field对象的一一个数组

那些方式能获得Class

1.在Object类中定义了以下的方法,此方法将被所有子类继承

public final native Class<?> getClass();
User user = new User();
user.getClass();

以上的方法返回值的类型是一-个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。

2.已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

Class c1 = Class.forName("com.annotation.User");

3.若已知具体的类,通过类的class属性获取, 该方法最为安全可靠,程序性能最高。

Class aClass = User.class;

4.基本类型的Type

5.类加载器ClassLoader

代码练习

public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {Person person = new Student();System.out.println("这个人是"+person.name);//方式一:通过对象获得Class c1 = person.getClass();System.out.println(c1.hashCode());//方式二:forName获得Class c2 = Class.forName("com.annotation.Student");System.out.println(c2.hashCode());//方式三:通过类名.classClass<Student> c3 = Student.class;System.out.println(c3.hashCode());//方式四:基本内置类型的包装类都有一个Type属性//限制就是只有基本类型可以这样Class c4 = Integer.TYPE;System.out.println(c4);//获得父类类型Class c5 = c1.getSuperclass();System.out.println(c5);}
}
class Person{public String name;public Person() {this.name = name;    }public Person(String name) {this.name = name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}
}
class Student extends Person{public Student() {this.name= "学生";}
}
class Teacher extends Person{public Teacher() {this.name= "老师";}
}

那些类型可以有Class对象

➢class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
➢interface: 接口
➢[]:数组
➢enum:枚举
➢annotation: 注解@interface
➢primitive type:基本数据类型
➢void

测试

//所有类型的Class对象
public class Demo2 {public static void main(String[] args) {Class c1 = Object.class;//类的Class对象Class c2 = Comparable.class;//接口的Class对象Class c3 = String[].class;//数组的Class对象Class c4 = int[][].class;//二维数组的Class对象Class c5 = Override.class;//注解类型的Class对象Class c6 = ElementType.class;//枚举类型的Class对象Class c7 = Integer.class;//Integer类型的Class对象Class c8 = void.class;//void类型的Class对象Class c9 = Class.class;//ClassSystem.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 java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class

分析

我们这两个不同的数组,是不是同一个Class

int[] a =new int[10];
int[] b =new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());

结果是一样的

只要元素类型与维度一样,就是一个Class

类加载内存分析

java内存

类加载

**➢加载:**将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,
然后生成一个代表这个类的java.lang.Class对象.
**➢链接:**将Java类的_ C进制代码合并到JVM的运行状态之中的过程。
**➢验证:**确保加载的类信息符合JVM规范,没有安全方面的问题
➢准备: 正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
**➢解析:**虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
➢初始化:
➢执行类构造器< clinit> ()方法的过程。类构造器< clinit> ()方法是由编译期自动收集类中所有类变量的赋值动作和静态
代码块中的语句合并产生的。(类构造 器是构造类信息的,不是构造该类对象的构造器)。
➢当初始化- 个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
➢虚拟机会保证-一个类的< clinit> ()方法在多线程环境中被正确加锁和同步。

测试

public class Demo3 {public static void main(String[] args) {A a = new A();System.out.println(a.m);}
}
class A{static {System.out.println("A类静态代码块初始化");m =300;}static int m = 100;public A() {System.out.println("A类的无参构造方法执行");}
}
结果:
A类静态代码块初始化
A类的无参构造方法执行
100

疑问,static修饰的m是100,但是static初始化的时候没有修改这个值么?还是说静态代码块比这个

static int m = 100;先执行呢?

因为我们合并代码块的时候,

JVM是这样合并的

m=300

m=100

前边的被覆盖了所以最后打印的m=100

再用UP主的官方语句

类的初始化

类的主动引用(一定会发生类的初始化)

➢当虚拟机启动,先初始化main方法所在的类
➢new-一个类的对象
➢调用类的静态成员(除了final常量)和静态方法
➢使用java.lang.reflect包的方法对类进行反射调用
➢当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

代码测试

public class Demo4 {static {System.out.println("main类被加载了");}public static void main(String[] args) throws ClassNotFoundException {//1.主动引用Son son  = new Son();//反射也会产生主动引用Class.forName("com.annotation.Son");}
}
class Father{static int b =2;static {System.out.println("父类被加载了!!!");}
}
class Son extends Father{static {System.out.println("子类被加载了");}static int m =100;static final int M = 1;
}
结果
main类被加载了
父类被加载了!!!
子类被加载了

➢类的被动引用(不会发生类的初始化)

➢当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
➢通过数组定义类引用,不会触发此类的初始化
➢引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

//不会产生类的引用方法
System.out.println(Son.b);
结果:调用了父类的静态变量,子类没被初始化
main类被加载了
父类被加载了!!!
2
Son[] array = new Son[5];
结果
main类被加载了
只有主类加载,父类子类都没有被加载,都没有初始化
System.out.println(Son.M);
结果
main类被加载了
1
没有初始化,常量在连接的时候就已经存入调用类的常量池中了

类加载器

作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一-个代表这个类的java.lang(Class对象)作为方法区中类数据的访问入口。
类缓存:

标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维
持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

获取类加载器

public class Demo5 {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++)ClassLoader parent1 = parent.getParent();System.out.println(parent1);//测试当前类是那个加载器获取的ClassLoader classLoader = Class.forName("com.annotation.Demo5").getClassLoader();System.out.println(classLoader);classLoader = Class.forName("java.lang.Object").getClassLoader();System.out.println(classLoader);//如何获取系统类加载器可以加载的路径System.out.println(System.getProperty("java.class.path"));}
}结果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@6d6f6e28
null
//这个跟我们的系统类的加载器一样
sun.misc.Launcher$AppClassLoader@18b4aac2
null
C:\Program Files\Java\jdk1.8.0_121\jre\lib\charsets.jar

这里可以再去看一下双亲委派机制

简单来说就是,我们自己写了一个java.lang.string的一个类,那我们写的这个类会不会运行呢?

类加载器就会一步一步往上找

先找用户类加载器—->扩展类加载器—–>根加载器

欸,他发现根加载器有这个java.lang.string,那么鉴于安全问题,我们手写的就不会运行

类运行时的结构

通过反射获取运行时类的完整结构
Field、Method、Constructor、 Superclass、 Interface、 Annotation
➢实现的全部接口
➢所继承的父类
➢全部的构造器
➢全部的方法
➢全部的Field
➢注解

代码测试

public class Demo6 {public static void main(String[] args) throws ClassNotFoundException {Class c = Class.forName("com.annotation.User");//获得类的名字System.out.println(c.getName());System.out.println(c.getSimpleName());结果com.annotation.UserUser//获得类的属性Field[] fields=c.getFields();//只能找到public的属性fields = c.getDeclaredFields();for (Field field:fields){System.out.println(field);}结果:private java.lang.String com.annotation.User.nameprivate int com.annotation.User.idprivate int com.annotation.User.ageMethod[] methods = c.getMethods();//获取本类以及父类的全部的public方法for (Method method:methods){System.out.println("正常的:" + method);}methods = c.getDeclaredMethods();//获取本类的所有方法for (Method method:methods){System.out.println("getDeclaredMethods:" + method);}其他的方法我们可以自行参考    }
}

动态创建对象

➢创建类的对象:

调用Class对象的newInstance()方法
➢1)类必须有一-个无参数的构造器。
➢2) 类的构造器的访问权限需要足够
思考?难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
➢步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构
造器
2)向构造器的形参中传递一一个对象数组进去,里面包含了 构造器中所需的各个参数。
3)通过Constructor实例化对象

代码测试

public class Demo07 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {//获取Class对象Class c = Class.forName("com.annotation.User");//构造一个对象User user = (User) c.newInstance();//本质是调用了我们User的无参构造器,如果没有就会报错的System.out.println(user);//通过构造器创建对象   没有我们的无参构造,这个也可以动态创建对象Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class, int.class);User user2 = (User) declaredConstructor.newInstance("花花", 001, 18);System.out.println(user2);}
}

通过反射调用普通方法

User user3 = (User) c.newInstance();//本质是调用了我们User的无参构造器,如果没有就会报错的Method setName = c.getDeclaredMethod("setName", String.class);setName.invoke(user3,"嘻嘻嘻");//激活System.out.println(user3.getName());

通过反射操作属性

User user4 = (User) c.newInstance();
Field name = c.getDeclaredField("name");//获得name属性
name.set(user4,"画画");
System.out.println(user4.getName());

Exception in thread “main” java.lang.IllegalAccessException: Class com.annotation.Demo07 can not access a member of class com.annotation.User with modifiers “private”

说明我们权限不够,这个属性是私有的

利用反射改进

User user4 = (User) c.newInstance();
Field name = c.getDeclaredField("name");//获得name属性
name.setAccessible(true);//关闭程序的安全检测,但是会降低效率
name.set(user4,"画画");
System.out.println(user4.getName());

➢setAccessible作用是 启动和禁用访问安全检查的开关。
➢参数值为rue则指示反射的对象在使用时应该取消Java语言访问检查。
➢提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
➢使得原本无法访问的私有成员也可以访问
➢参数值为false则指示反射的对象应该实施Java语言访问检查

获取泛型信息

➢Java采用泛型擦除的机制来引入泛型, Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
➢为了通过反射操作这些类型, Java新增了ParameterizedType , GenericArrayType,
TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.
ParameterizedType :表示-种参数化类型,比如Collection
➢GenericArrayType : 表示一-种元素类型是参数化类型或者类型变量的数组类型
➢**TypeVariable :是各种类型变量的公共父接口
WildcardType

Java的注解和反射相关推荐

  1. Java基础-注解和反射

    Java基础-注解和反射 前言 对于注解,我主要还是在自定义APT还有运行时反射获取类来让自己能够构建出复用性更高的代码逻辑. 知识点1-注解: 注解的应用场景由元注解@Retention来进行指定, ...

  2. Java基于注解和反射导入导出Excel

    代码地址如下: http://www.demodashi.com/demo/11995.html 1. 构建项目 使用Spring Boot快速构建一个Web工程,并导入与操作Excel相关的POI包 ...

  3. 厚积薄发打卡Day24 :狂神说Java之注解与反射<全网最全(代码+笔记)>

    原视频地址: [狂神说Java]注解和反射,强烈推荐大家学习 什么是注解 什么是注解? Annotation是从JDK5.0开始引入的新技术. Annotation的作用: 不是程序本身,可以对程序作 ...

  4. (Java)注解和反射

    文章目录 注解和反射 一. 注解 1.1 元注解 1.2 内置注解 1.3 自定义注解 二. 反射 2.1 什么是反射 2.2 Class类 2.3 创建Class类的方式 2.4 所有类型的Clas ...

  5. Java:注解和反射

    (一)注解 1注解入门 Annotation是jdk1.5开始引入的新技术. Annotation的作用: (1)不是程序本身,可以对程序作出解释: (2)可以被其他程序(例如编译器)读取. Anno ...

  6. Java中注解与反射的使用方法及场景,强行解释一波!

    作者:BudingCode blog.csdn.net/m0_55221239/article/details/115025182 注解 注解定义 Java 注解(Annotation)又称 Java ...

  7. java基础- 注解和反射

    1. 注解(Annotation) 1. 什么是注解 Annotation是从JDK5.0开始引入的新技术. Annotation的作用 : 不是程序本身,可以对程序作出解释.(这一点和注释(comm ...

  8. java 反射 注解 运用_Java注解与反射的使用

    打开 Eclipse,新建 Java 项目"注解与反射",在 src 下右键并建立包 "注解与反射",在包下右键并建立 Annotation (注解)文件,名称 ...

  9. Java学习笔记7-2——注解与反射

    目录 理解 Class 类并获取 Class 实例 Class类 获取 Class 类的实例 哪些类型可以有Class对象 所有类型的Class对象 从内存角度分析类加载[重点] 类加载的过程 什么时 ...

  10. 04Java注解和反射

    Java注解和反射 Java框架的底层即为Java的注解和反射 注解(开胃菜) 注释:给开发人员阅读,不算在程序之内 注解:可以给开发人员阅读,也可以给程序阅读,也可以被其他的程序阅读,也不算在程序之 ...

最新文章

  1. 【基础积累】1x1卷积到底有哪些用处?
  2. AndroidStudio中Attatch debugger to Android Ptocess时 Choose Process后OK是灰色的
  3. 如何理解Minor/Major/Full GC
  4. 井下关于风速的规定_矿井有害气体最高允许浓度、温度、风速的规定
  5. 47 CO配置-控制-利润中心会计-设置实际数据的控制参数
  6. LeetCode-150-Evaluate Reverse Polish Notation
  7. 【华为云技术分享】处理器存储模型概述(2)
  8. (37)System Verilog类外方法示例
  9. 【ASP.NET 问题】IIS发布网站后出现 “处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误“的解决办法
  10. mysql nlssort_Oracle中文排序 NLSSORT
  11. 领导说“辛苦了”,怎么回才显情商高
  12. spring开发常用的
  13. Typora中的emoji图标标签
  14. 手把手教你写保研简历|计算机保研|保研夏令营文书写作|简历模板
  15. ImageView中动态设置图片
  16. IT咨询,从问题到主义
  17. 关于H.264 x264 h264 AVC1
  18. 二层网络的未来?starkgate 带你体验二层桥接
  19. Alessandro De Luca 大神级任务广义动量应用于动力学解决方案
  20. 前台调用后台方法,无刷新更新数据

热门文章

  1. linux 解压 7z 乱码,7z-linux下解决中文名乱码的终极办法
  2. 服务器linux命令aux,Linux查看所有进程ps -aux命令介绍
  3. kruskal java_kruskal算法 源码(java)
  4. python爬虫如何连接数据库_Python爬虫框架和数据库连接
  5. Node.js:Webpack
  6. SQL:pgsql中时间戳转换为整数
  7. pscp使用详解 Windows与Linux文件互传工具
  8. Java中的几种设计模式:创建型模式,结构型模式
  9. python_numpy_中的matrix与array的区别
  10. ML/DL-复习笔记【一】- 数学基础(线性代数、概率论、数值分析)