Java基础复习 Day 27
注解与反射 1
1. Java Annotation
(1)关于注解
-
Annotation是JDK1.5之后出现的新技术
-
Annotation作用:
- 对程序本身作出解释(类似于comment)
- 可以被其他程序(编译器等)所读取
-
Annotation格式:
以”@注释名“在代码中存在,还可以添加一些参数值:
例:@SuppressWarnings(value = “unchecked”)
-
Annotation使用域
package,class,method,field上边相当于添加了附加信息然后通过反射机制对编程而实现对这些数据的访问。
(2) 内置注解
-
@Override:只用来修饰方法,表示一个派生类想要重写基类的方法
-
@Deprecated:可用来修饰方法,属性,类,但并不鼓励使用,通常是因为很危险或者有更好的选择。
-
@SuppressWarnings() 用来抑制编译时的警告信息 与前两个注解不同的是,在该注解中必须标注参数来说抑制警告的具体信息
关键字value 用途
all 禁止所有警告
boxing 禁止显示有关装箱/拆箱操作的警告
cast 禁止显示与投放操作相关的警告
dep-ann 禁止显示有关已过时注释的警告
deprecation 抑制有关弃用的警告
fallthrough 抑制与switch语句中缺少中断有关的警告
finally 抑制相对于finally块的警告不返回
hiding 抑制相对于隐藏变量的本地人的警告
incomplete-switch 禁止显示与switch语句中缺少条目有关的警告(枚举)
nls 禁止显示有关非nls字符串文字的警告
null 抑制与空分析有关的警告
rawtypes 在类参数上使用泛型时,抑制相对于非特定类型的警告
restriction 抑制有关使用不推荐或禁止使用的引用的警告
serial 禁止显示有关可序列化类的缺少serialVersionUID字段的警告
static-access 抑制有关错误静态访问的警告
synthetic-access 抑制有关内部类未优化访问的警告
————————————————
原文链接:https://blog.csdn.net/JGMa_TiMo/article/details/104835322
(3)元注解
元注解的作用就是负责注解其他注解,java提供了四个标准的meta-annotation元注解用来对其他注解进行说明
- @Documented:说明该注解将会被记录在javadoc文档中
- @Retention:用于描述需要在什么级别去保存该注解信息(SOURCE<CLASS<RUNTIME一般标记成runtime让运行时也可识别注解)
- @Target: 用于描述注解的使用范围
- @Inherited:说明子类可以继承父类的该注解
(4)自定义注解
使用@interface来自定义注解,自动继承了package java.lang.annotation.Annotation接口
-
自定义注解的格式 public @interface 注解名{};
package com.kou.annotation; import java.lang.annotation.*; public class Demo1 { //注解可以显示的赋值,如果没有默认值就必须要给注解赋值 //参数没有顺序 @Myannovation(name = "Karen",age = 30) public void test1(){ } //如果参数只有一个参数,推荐用value,这样在调用的时候就可以省略关键字value直接把参数的值赋值即可,否则必须加参数名 @Myannovation2(value = "name") public void test2(){} } @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited @interface Myannovation{ //注解的参数:参数类型 参数名() String name() default ""; int age(); int id() default -1; } @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited @interface Myannovation2{ //注解的参数:参数类型 参数名() String value(); }
2. Java Reflection
1.Java反射机制
- Java反射机制:是java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 静态语言:程序在运行中不改变其自身结构 java,c,c++
- 动态语言:程序在运行中改变自身结构javascript,python,等(比如javascript中变量var在执行过程中可以任意 从string变成别的类型)
-
Java反射运行的方式。
在定义好一个类后。加载完一个类后,类就在内存的堆内存的方法区产生一个.Class对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个类对象看到类的结构,这个类对象就像一面镜子,透过这个镜子看到所有的类的信息,因此我们形象地称之为反射。
-
正常的访问类的方式
引入需要的“包类”名称⇒通过new实例化⇒取得实例化对象
-
反射访问方式
实例化对象⇒getClass()方法⇒得到完整的“包类”名称
-
-
Java反射机制提供的机能:
在程序运行时判断任意一个对象所属的类,
在程序运行时构造任意一个类的对象
在运行时判断任意一个类所具有的的成员方法和变量
在运行时调用任意一个对象的成员变量和方法
生产动态代理
在运行时处理注解
-
Java反射机制的优缺点
优点:可以动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响。使用反射基本上就是一种解释操作,我们可以告诉jvm,让他去做什么,这种操作就比较慢(比如比new慢多了)
-
Class类
在Object类中定义了以下的方法,此方法被所有子类继承
public final native Class<?> getClass();
以上的方法返回值就是一个Class类,此类是Java反射的源头。对象照镜子后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了哪些接口,对于每个类而言,JRE都为其保留一个不变的Class类型的对象,一个Class对象包含了特定某个结构(class、interface,enum,annotation,primitive type,void。。)的有关信息)
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("this is " + p1.name);
//方式一,通过对象来获得
Class c1 = p1.getClass();
//方式二:通过Class静态方法获得Class对象
Class c2 = Class.forName("com.kou.reflection.Student");
//方式三:通过类名.class
Class c3 = Student.class;
System.out.println(c1.hashCode());//1735600054
System.out.println(c2.hashCode());//1735600054
System.out.println(c3.hashCode());//1735600054
//基本内置类型的包装类都有一个Type属性 也可以获得一个Class类对象 但是仅对基本内置类型有用
Class<Integer> cI1 = Integer.TYPE;
System.out.println(cI1.hashCode());//21685669s
Class c5 = c1.getSuperclass();
System.out.println(c5);//class com.kou.reflection.Person
}
}
class Person{
public String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person {
public Student() {
this.name = "Student";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "Teacher";
}
}
-
哪些类型有Class类
-
class:内部类,成员类(内部静态,成员内部类),局部内部类,匿名内部类
-
interface:接口
-
[]数组:
-
enum:枚举
-
annotation:@interface注解
-
primitive数据:基本类型的数据类型
-
void
Class<Object> c1 = Object.class;//class java.lang.Object Class<Comparable> c2 = Comparable.class;//interface java.lang.Comparable Class<String[]> c3 = String[].class;//class [Ljava.lang.String; Class<int[][]> c4 = int[][].class;//class [[I Class<Override> c5 = Override.class;//interface java.lang.Override Class<ElementType> c6 = ElementType.class;//class java.lang.annotation.ElementType Class<Integer> c7 = Integer.class;//class java.lang.Integer Class<Void> c8 = void.class;//void Class<Class> c9 = Class.class;//class java.lang.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());//1735600054 System.out.println(b.getClass().hashCode());//1735600054
-
类的加载过程:类的加载(Load) ⇒ 类的链接(Link) ⇒ 类的初始化(Initialize)
类的加载:将类的class文件读入内存,并为其创建一个java.lang.Class对象。此过程由类的加载器完成
类的链接:将类的二进制数据合并到JRE中。
类的初始化:JVM负责对类进行初始化
-
public class Test05 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.a);
//A类的静态代码块初始化
//A类的无参构造方法初始化
//10 (a=0,a=300,a=10 静态区进行一个数据最终整合,为静态变量做准备)
}
}
class A {
static {
System.out.println("A类的静态代码块初始化");
a = 300;
}
static int a = 10;
String name;
public A() {
System.out.println("A类的无参构造方法初始化");
}
}
- 类的初始化
-
类的主动引用
public class Test06 { static { System.out.println("Main方法所在类的静态代码块执行"); } public static void main(String[] args) throws ClassNotFoundException { Zi zi = new Zi(); //Main方法所在类的静态代码块执行 //父类静态代码块执行 //子类静态代码块执行 Class c1 = Class.forName("com.kou.reflection.Zi"); //Main方法所在类的静态代码块执行 //父类静态代码块执行 //子类静态代码块执行 } } class Fu{ static { System.out.println("父类静态代码块执行"); } static int n = 400; } class Zi extends Fu { static { System.out.println("子类静态代码块执行"); m = 300; } static int m = 100; static final int a = 200; }
-
类的被动引用
public class Test06 { static { System.out.println("Main方法所在类的静态代码块执行"); } public static void main(String[] args) throws ClassNotFoundException { //类的被动加载,子类调用父类的静态变量时,子类没有被加载 //System.out.println(Zi.n); //Main方法所在类的静态代码块执行 //父类静态代码块执行 //400 //通过数组定义类引用 Zi[] arr = new Zi[10]; //引用常量 System.out.println(Zi.a); //200 } } class Fu{ static { System.out.println("父类静态代码块执行"); } static int n = 400; } class Zi extends Fu { static { System.out.println("子类静态代码块执行"); m = 300; } static int m = 100; static final int a = 200; }
-
类加载器的作用
public static void main(String[] args) {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取系统类加载器的父类-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类-->根加载器(null是因为这是c++编写的所以获取不到)
ClassLoader grandparent = parent.getParent();//sun.misc.Launcher$ExtClassLoader@677327b6
System.out.println(grandparent);//null
//测试当前类是由哪个类加载器加载的
//测试jdk的Object类时由哪个类加载器加载的
ClassLoader currClassLoader = Class.forName("com.kou.reflection.Test07").getClassLoader();
System.out.println(currClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader loaderForObject = Class.forName("java.lang.Object").getClassLoader();
System.out.println(loaderForObject);//null
}
本篇课程来源均是狂神说,图片来自于bilibili的视频截图,如有侵权请务必联系。
视频地址请点击这里