注解和反射
注解
Annotation是jdk5.0引入的概念
它的作用:
- 不是程序本身,可以对程序做出解释(和注释(comment)相似)
- 可以被其他程序(如,编译器)读取。
格式:
以“@参数名”在程序中存在,还可以添加一些参数值
适用范围:
可以附加在package,class,method,field等上面,相当于给他们添加了额外的信息,我们可以通过反射编程机制实现对这些元数据的访问。
内置注解
@Override
定义在java.lang.Override中,用于修饰方法,表示一个方法声明重写超类中的另一个方法声明。
@Deprecated
定义在java.lang.Deprecated ,用于修饰方法,属性,类,表示不鼓励程序猿使用这样的元素,该方法废弃或有更好的选择。
@SuppressWarnings
定义在java.lang.SuppressWarnings,用于抑制编译时的警告信息
与前两个不同,次注解需要添加一个参数才能正常使用,这些参数是定义好的:(根据警告类型的不同,参数不同)
@SuppressWarnings(“All”) 忽略全部警告
@SuppressWarnings(“unchecked”)
告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
@SuppressWarnings(“serial”)
如果编译器出现这样的警告信息: The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long
,使用这个注释将警告信息去掉。 · @SuppressWarnings(“deprecation”)
如果使用了使用 @Deprecated注释的方法,编译器将出现警告信息。 使用这个注释将警告信息去掉。 · @SuppressWarnings(“unchecked”, “deprecation”)
告诉编译器同时忽略 unchecked和deprecation的警告信息。 · @SuppressWarnings(value={“unchecked”, “deprecation”})
等同于 @SuppressWarnings(“unchecked”, “deprecation”)
除此之外,其他的类型的警告忽略参考
链接: 参考链接.
元注解
元注解的作用是负责注解其他的注解,Java提供了4个Meta-annotation类型,他们被用来提供对其他annotation类型做说明。存储于 java.lang.annotation包下
@Target
用于描写注解适用范围(即注解可以用于什么地方)
@Retention
用于描述注解的生命周期
(SOURCE<CLASS<RUNTIME)
@Document
说明该注解包含在javadoc中
@Inherited
说明子类可以继承父类中的该注解
import org.junit.Test;
import java.lang.annotation.*;
@MyAnnointion
public class Meta_Annointion {
//@MyAnnointion----会报错,没定义成员属性上
String name;
@Test
@MyAnnointion
public void test(){
}
}
//定义一个注解
//定义他的适用范围------定义在方法和类上
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//定义生命周期(注解在哪一阶段有效)
@Retention(RetentionPolicy.RUNTIME)
//(SOURCE<CLASS<RUNTIME)
//定义将我们的注解生成在javadoc中
@Documented
//子类可以继承父类的注解
@Inherited
@interface MyAnnointion{
}
自定义注解
关键字 @Interface ,自动继承了java.lang.annointion.Annotation接口
@interface用来声明一个注解,格式:public@interface注解名{定义内容} 其中的每一个方法实际上是声明了一个配置参数.
方法的名称就是参数的名称 返回值类型就是参数的类型(返回值只能是基本类型,Class, String, enum).
可以通过default来声明参数的默认值 如果只有一个参数成员,一般参数名为value
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值,
反射Reflection
反射机制允许程序在执行期间借助Reflection API 获取任何类的内部信息,并能直接操作对象的实行和方法。
加载完类后,在堆内存的方法区中 就产生了一个Class对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,我们可通过此对象看见类的完整信息,就想一面镜子,通过它我们可以看到累的结构,我们形象的称其为“反射”
Class c = Class.forName(“java.lang.String”)
Class c = Class.getClass()
反射机制作用:
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解 生成动态代理
等。。。。。
优点:
可以实现动态创建对象和编译,有很大的灵活性。
缺点:
会拖慢性能,反射是解释性的操作,这类操作总慢于直接执行相同的操作
class类的常用方法
class获得实例的方法
public class ClassDome {
public static void main(String[] args) {
//通过对象获得---------注意一个类只有一个Class对象 hashcode相同
Class c1 = Student.getClass();
System.out.println(c1.hashCode());
//通过forName 包名 获得
Class c2 = Class.forName("java.nuc.src.Student");
System.out.println(c2.hashCode());
//通过类名.Class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
// 因为基本内置类型的包装类都有个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4.hashCode());//输出的是Integer的Class 和上面不同
//通过子类获得父类
class c5 = c1.getSuperclass();
System.out.println(c5.hashCode());
}
}
class Person{
private String Name;
private int id;
public Person() {
}
public Person(String name, int id) {
Name = name;
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
class Student extends Person{
public Student(){
this.name="学生";
}
}
有哪些类型有Class对象
public Test {
public static void main(String[] args) {
Class c1 = Objcet.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 c1 = Class.class;//Class
}
}
类加载器
作用:将Class文件字节码内容加载到内存中。并将这些静态数据转化成方法取得运行时那数据结构,然后在堆中生成一个代表这个累的java.lang.Class对象,作为方法区中类数据的访问入库
类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,他将维持加载(缓存一段时间),不过jvm的垃圾回收机制可以回收这些Class对象。
分类:
类加载过程
- 加载
- 链接
2.1 验证
2.2 准备
2.3 解析 - 初始化
public class Text {
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 grandpa = parent.getParent();
System.out.println(grandpa);
//测试当前类是由那个类加载器加载的
Class c1 = Text.class;
ClassLoader c = c1.getClassLoader();
System.out.println(c);
//测试JDK内部类是由那个类加载器加载的
ClassLoader c2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(c2);
//获得系统类加载器可以加载得路径
System.out.println(System.getProperty("java.class.path"));
}
}
双亲委派机制:
流程图
当一个编程文件要被加载时。不考虑我们自定义的类加载器情况下,首先会在AppClassLoader中判断是否加载过,如果加载过,那就无需再次加载了。如果没有加载,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上,直到到达BootstrapclassLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
好处:可以使我们的程序更安全,如果有人想替换系统级别的类:如String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会
再去加载,从一定程度上防止了危险代码的植入。
通过反射获取类的钢结构:
新建一个我们要反射的类
public class User {
public String name;
private String privateName;
private Integer id;
public User() {
}
public User(String name, String privateName, Integer id) {
this.name = name;
this.privateName = privateName;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrivateName() {
return privateName;
}
public void setPrivateName(String privateName) {
this.privateName = privateName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", privateName='" + privateName + '\'' +
", id=" + id +
'}';
}
}
进行反射获得结构
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) throws NoSuchMethodException {
//定义要反射累的class对象
Class c = User.class;
//获取类的名字
System.out.println(c.getName());//包名+类名
System.out.println(c.getSimpleName());//包类名
System.out.println("====================");
//获得累的属性
Field[] fields = c.getFields();//只能获得public属性
for (Field f:fields){
System.out.println(f);
}
Field[] field = c.getDeclaredFields();//获得全部属性
for (Field f:field){
System.out.println("getDeclaredFields======"+f);
}
//获得类的方法
Method[] methods = c.getMethods();//获得本类及父类的全部public
for (Method f:methods){
System.out.println("g"+f);
}
Method[] method = c.getDeclaredMethods();//获得本类的全部方法
for (Method f:method){
System.out.println("getDeclaredMethods======"+f);
}
System.out.println("=============");
//获得指定的方法======传入方法名和参数类型
Method s1= c.getMethod("getName", null);
Method s2= c.getMethod("setName", String.class);
System.out.println(s1);
System.out.println(s2);
//获得构造器
Constructor[] constructors = c.getConstructors();
for (Constructor con:constructors){
System.out.println(con);
}
Constructor[] constructor = c.getDeclaredConstructors();
for (Constructor con:constructor){
System.out.println("==="+con);
}
System.out.println("=========");
//获得指定的构造器
Constructor constructor1 = c.getConstructor(String.class,String.class,Integer.class);
Constructor constructor2 = c.getConstructor();
System.out.println(constructor1);
System.out.println(constructor2);
}
}
动态创建对象执行方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//通过反射,动态创建对象
public class ReflectionDome {
public static void main(String[] args) throws Exception{
//获得class对象
Class c2 = User.class;
//构造一个对象
User user = (User) c2.newInstance();
System.out.println(user);
//结果为: User{name='null', privateName='null', id=null} 创建的是无参对象
//创建有参对象----通过构造器
Constructor constructor=c2.getConstructor(String.class,String.class,Integer.class);
User user1=(User) constructor.newInstance("我","你",18);
System.out.println(user1);
//通过反射调用普通方法
//构造一个对象
User user2 = (User) c2.newInstance();
//通过反射获取一个方法
Method m1= c2.getMethod("setName", String.class);
//invoke(对象,“方法的值”)
m1.invoke(user2, "鸿蒙系统");
System.out.println(user2.getName());
//通过反射操作属性
Field name =c2.getField("name");
//不能直接操作私有属性,要关闭安全检测,-----》属性或方法.setAccessible(true)
name.setAccessible(true);
name.set(user2,"华为-鸿蒙harmonyos");
System.out.println(user2.getName());
}
}
反射操作泛型
Java采用泛型擦除的机制引入泛型,java的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,以编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,java引入了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归到class类中的类型但又和原始类型齐名的类型
package reflectiondome;
import org.junit.Test;
import javax.lang.model.element.TypeParameterElement;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Dome {
//获取参数
public void test01(Map<Student, String> map, List<Student> list) {
System.out.println("test01");
}
//获取返回值
public Map<Student, String> test02() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws Exception {
//先获得方法
Method method = Dome.class.getMethod("test01", Map.class, List.class);
Type[] type = method.getGenericParameterTypes();//获取带参数的泛型
for (Type a : type) {
System.out.println(a);//输出的是参数化类型
if (a instanceof ParameterizedType)//判断是否属于参数化类型
{
//获得其真实类型
Type[] type1 = ((ParameterizedType) a).getActualTypeArguments();
for (Type t : type1) {
System.out.println(t);
}
}
}
//返回值类型
Method method1 = Dome.class.getMethod("test02",null);
Type type3 = method1.getGenericReturnType();
if (type3 instanceof ParameterizedType){
Type[] types = ((ParameterizedType) type3).getActualTypeArguments();
for (Type type1:types){
System.out.println(type1);
}
}
}
}
通过反射操作注解
package reflectiondome;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//通过反射操作注解
public class test {
public static void main(String[] args) throws NoSuchFieldException {
Class c1 = Person1.class;
//通过反射操作注解
Annotation[] annotations = c1.getAnnotations();//通过反射获得所有注解
//获得注解的值
System.out.println("=======获得注解的值=========");
for (Annotation annotation :annotations){
System.out.println(annotation);
}
//获得注解的value值
System.out.println("=======获得注解的value值=========");
TypeClass annotation = (TypeClass)c1.getAnnotation(TypeClass.class);
String name = annotation.value();
System.out.println(name);
//获得类指定的注解
//1 获得注解算在的方法或属性
Field f= c1.getDeclaredField("id");
FieldClass annotation1 = f.getAnnotation(FieldClass.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.length());
System.out.println(annotation1.type());
}
}
@TypeClass("db_Person")
class Person1 {
@FieldClass(columnName = "db_name",type = "String",length = 10)
private String name;
@FieldClass(columnName = "db_id",type = "int",length = 10)
private int id;
public Person1() {
}
public Person1(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
//定义一个类注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeClass{
String value();
}
//定义一个属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldClass{
String columnName();
String type();
int length();
}