注解与反射

注解

内置注解

  • @Override: 重写超类的方法

  • @Deprecated: 弃用

  • @SuppressWarnings: 抑制编译时的警告信息

元注解

  • @Target: 注解的范围

  • @Retention: 注解的生命周期

    • SOURCE<CLASS<RUNTIME

  • @Document: 注解将包含在javadoc中

  • @Inherited: 子类可以继承父类的注解

import java.lang.annotation.*;

public class TestStart {
//没有参数
@myAnnotation
public void test1(){}
//有参数
@myAnnotation2(age = 18)
public void test2(){}
//有参数
@myAnnotation3("test3")
public void test3(){}
}

//没有参数
@Target({ElementType.METHOD})  //只能定义在方法上,需要在类上注解则加上参数即可
@Documented
@Retention(RetentionPolicy.RUNTIME) // 在运行时该注解也有效
@Inherited
@interface myAnnotation{}

//有参数
@Target({ElementType.METHOD})  //只能定义在方法上,需要在类上注解则加上参数即可
@Documented
@Retention(RetentionPolicy.RUNTIME) // 在运行时该注解也有效
@Inherited
@interface myAnnotation2{
//参数写法: 类型 + 参数名 + ()
int age(); //此时没有默认值就需要写参数的值
int id() default 0;
String name() default "";
String[] name2() default {};
}

//有参数
@Target({ElementType.METHOD})  //只能定义在方法上,需要在类上注解则加上参数即可
@Documented
@Retention(RetentionPolicy.RUNTIME) // 在运行时该注解也有效
@Inherited
@interface myAnnotation3{
//只有一个参数且为value(),此时用注解时value的参数名可以省略
String value();
}

反射

  • Reflection是java视为动态语言的关键

  • 反射机制允许程序在执行期间借助Reflection API获取任何类的内部信息,并可以直接操作任意对象的内部属性及方法

  • 一个类只有一个Class对象

  • 反射是实例对象获取完整的类的信息

  • 优缺点

    • 动态创建对象和编译,具有很高的灵活性

    • 对性能有影响,因为反射基本是一种解释操作,得告诉JVM我们需要做什么才能满足我们的要求

public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException {
  //通过Class.forName反射获取类
  Class c1 = Class.forName("User");
  System.out.println(c1);
  //一个类只有一个Class对象,证明
  Class c2 = Class.forName("User");
  Class c3 = Class.forName("User");
  Class c4 = Class.forName("User");
  System.out.println(c1.hashCode());
  System.out.println(c2.hashCode());
  System.out.println(c3.hashCode());
  System.out.println(c4.hashCode());
  //每个类都继承Object,所以都有getClass()获取Class对象
  User user = new User();
  Class c5 = user.getClass();
  System.out.println(c5);
  System.out.println(c5.hashCode());
}
}

class User{
private Integer id;
private String name;

public User() {
}

public User(Integer id, String name) {
  this.id = id;
  this.name = name;
}

public void setId(Integer id) {
  this.id = id;
}

public void setName(String name) {
  this.name = name;
}

public Integer getId() {
  return id;
}

public String getName() {
  return name;
}

@Override
public String toString() {
  return "User{" +
          "id=" + id +
          ", name='" + name + '\'' +
          '}';
}
}

反射获取类的方式

public class TestReflection_methods {
public static void main(String[] args) throws ClassNotFoundException {
  Student student = new Student("小红");
  //方式一: 通过实例获取
  Class c1 = student.getClass();
  System.out.println(c1.hashCode());
  //方式二: 通过.class获取
  Class c2 = Student.class;
  System.out.println(c2.hashCode());
  //方式三: 通过Class.forName
  Class c3 = Class.forName("Student");
  System.out.println(c3.hashCode());
  //方式四: 只有基本包装类才有
  Class c4 = Integer.TYPE;
  System.out.println(c4.hashCode());
  //获取父类的Class
  Class c5 = c1.getSuperclass();
  System.out.println(c5.hashCode());

}
}
class Person{
String name;
}
class Student extends Person{
public Student(String name){
  this.name = name;
}
}
import java.lang.annotation.ElementType;

public class TestReflection_all {
public static void main(String[] args) {
  Class c1 = Integer.class;  //基本类型
  Class c2 = String[].class; //一维数组
  Class c3 = int[][].class;  //二维数组
  Class c4 = Class.class; // Class
  Class c5 = ElementType.class; //枚举
  Class c6 = Override.class; //注解
  Class c7 = Comparable.class;//接口
  Class c8 = Object.class; // 类
  Class c9 = void.class;//void
  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);
}
}

获取类运行时的结构

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestReflection_get {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
  Class user = Class.forName("User");
  //获取类的名字
  System.out.println(user.getName());
  System.out.println(user.getSimpleName());
  //获取字段
  Field[] fields = user.getFields(); //获取public修饰的属性
  for (Field field : fields) {
      System.out.println(field);
  }
  fields = user.getDeclaredFields();//获取所有的属性
  for (Field field : fields) {
      System.out.println(field);
  }
  //获取指定的字段
  Field name = user.getDeclaredField("name");
  System.out.println(name);
  //获取构造器
  Constructor[] constructors = user.getConstructors(); //获取public的构造器
  for (Constructor constructor : constructors) {
      System.out.println(constructor);
  }
  constructors = user.getDeclaredConstructors();//获取所有的构造器
  for (Constructor constructor : constructors) {
      System.out.println(constructor);
  }
  //获取指定的构造器
  Constructor constructor = user.getConstructor(Integer.class, String.class);
  System.out.println(constructor);
  //获取方法
  Method[] methods = user.getMethods();//获取public的方法
  for (Method method : methods) {
      System.out.println(method);
  }
  methods = user.getDeclaredMethods();//获取所有的方法
  for (Method method : methods) {
      System.out.println(method);
  }
  //获取指定的方法
  Method method = user.getMethod("setName",String.class);
  System.out.println(method);
}
}

获取实例(两种)

  • newInstance()

    • 类必须有一个无参构造

    • 类的构造器权限需能访问

  • 通过getDeclaredConstructor()获取构造器,再向构造器形传递一个对象数组进去,最后Constructor实例化对象

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test_Instance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
  //反射实例化(无参构造)
  Class user = Class.forName("User");
  User o = (User) user.newInstance();
  o.setName("hello Reflection");
  System.out.println(o.getName());
  //用有参构造
  Class user1 = Class.forName("User");
  Constructor declaredConstructor = user1.getDeclaredConstructor(Integer.class, String.class);
  User hello_reflection2 = (User)declaredConstructor.newInstance(18, "hello Reflection2");
  System.out.println(hello_reflection2.getName());
  //通过反射调用普通的方法
  Method method = user1.getDeclaredMethod("setName", String.class);
  method.invoke(hello_reflection2,"小红");
  System.out.println(hello_reflection2.getName());
  //通过反射获取private属性
  Field name = user1.getDeclaredField("name");
  name.setAccessible(true);//关闭安全机制
  name.set(hello_reflection2,"小岚");
  System.out.println(hello_reflection2.getName());
}
}

获取泛型的信息

  • java的泛型是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,一旦编译完成就擦除有关泛型的类型

  • 反射操作的获取泛型

    • ParameterizedType:参数化类型

    • GenericArrayType:泛型数组

    • TypeVariable:类型变量

    • WildcardType:代表一中种通配符类型表达式

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Map;

public class Test_fanxing {
//泛型在参数上
public void test1(ArrayList<String> arrayList, Map<String,Integer> map){

}
//泛型在返回值
public ArrayList<Integer> test2(){
  return null;
}
//返回值是泛型数组
public ArrayList<String>[] test3(){
  return null;
}
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
  Class c1 = Test_fanxing.class;
  Method t1 = c1.getMethod("test1", ArrayList.class, Map.class);
  //获取方法参数的类型
  Type[] types1 = t1.getGenericParameterTypes();
  for (Type type : types1) {
      System.out.println(type);
  }
  //获取方法参数里面泛型的类型
  types1 = t1.getGenericParameterTypes();
  for (Type type : types1) {
      if(type instanceof ParameterizedType){
          Type[] a1 = ((ParameterizedType) type).getActualTypeArguments();
          for (Type type1 : a1) {
              System.out.println(type1);
          }
      }
  }
  //获取返回值的泛型
  Method t2 = c1.getMethod("test2", null);
  Type type2 = t2.getGenericReturnType();
  System.out.println(type2);
  //获取返回值的泛型里面的参数
  if(type2 instanceof ParameterizedType){
      Type[] types = ((ParameterizedType) type2).getActualTypeArguments();
      for (Type type : types) {
          System.out.println(type);
      }
  }
  System.out.println("========");
  //泛型数组的判断
  Method test3 = c1.getMethod("test3", null);
  Type genericReturnType = test3.getGenericReturnType();
  if(genericReturnType instanceof GenericArrayType){
      System.out.println(genericReturnType);
  }

}
}

注解与反射

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
//想要获取哪个注解就获取反射的哪个变量、哪个方法,再用getAnnotation(?.class)获取
public class Annotation_Reflection {
public static void main(String[] args) throws NoSuchFieldException {
  Class c1 = Student2.class;
  //获取类上面的注解
  Annotation[] annotations = c1.getAnnotations();
  for (Annotation annotation : annotations) {
      System.out.println(annotation);
  }
  //获取指定注解的值
  myStudent annotation = (myStudent)c1.getAnnotation(myStudent.class);
  System.out.println(annotation.value());
  //获取注解字段
  Field id = c1.getDeclaredField("id");
  myField annotation1 = id.getAnnotation(myField.class);
  System.out.println(annotation1.columnName());
  System.out.println(annotation1.type());
  System.out.println(annotation1.length());
}
}
@myStudent("table_student")
class Student2{
@myField(columnName = "table_id",type = "int",length = 10)
private Integer id;
@myField(columnName = "table_age",type = "int",length = 3)
private Integer age;
@myField(columnName = "table_name",type = "varchar",length = 3)
private String name;
public Student2(){}

public Student2(Integer id, Integer age, String name) {
  this.id = id;
  this.age = age;
  this.name = name;
}

public void setId(Integer id) {
  this.id = id;
}

public void setAge(Integer age) {
  this.age = age;
}

@Override
public String toString() {
  return "Student2{" +
          "id=" + id +
          ", age=" + age +
          ", name='" + name + '\'' +
          '}';
}

public void setName(String name) {
  this.name = name;
}

public Integer getId() {
  return id;
}

public Integer getAge() {
  return age;
}

public String getName() {
  return name;
}
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface myStudent{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface myField{
String columnName();
String type();
int length();
}

性能对比

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test_performance {
//new实例
public static void test1(){
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 2000000; i++) {
user.setName("hello");
}
long end = System.currentTimeMillis();
System.out.println("new实例执行循环时间:"+(end-start)+"ms");
}
//反射实例
public static void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class user = Class.forName("User");
User user1 = (User)user.newInstance();
Method setName = user.getMethod("setName", String.class);
long start = System.currentTimeMillis();
for (int i = 0; i < 2000000; i++) {
setName.invoke(user1,"hello");
}
long end = System.currentTimeMillis();
System.out.println("反射实例执行循环时间:"+(end-start)+"ms");
}
//关闭安全检查反射实例
public static void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class user = Class.forName("User");
User user1 = (User)user.newInstance();
Method setName = user.getMethod("setName", String.class);
setName.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 2000000; i++) {
setName.invoke(user1,"hello");
}
long end = System.currentTimeMillis();
System.out.println("关闭安全检查反射实例执行循环时间:"+(end-start)+"ms");
}

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test1();
test2();
test3();
}
}

Java内存

堆: 存放new的对象和数组,可以被线程所共享,不会存放别的对象。

栈: 存放基本变量类型(包含变量的数值),引用对象的变量(是存放堆里面的地址)。

方法区: 存放class和static,可以被所有线程共享。

类的加载与ClassLoader

  • 加载:将class文件字节码内容加载到内存中,并将这些静态的数据换成方法区的运行数据结构,生成一个代表这个类的对象。

  • 链接:将java类的二进制代码合并到JVM的运行状态的过程

    • 验证: 确保加载的信息符合JVM的规范,没有安全方面的问题。

    • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区进行分配。

    • 解析:虚拟机的常量池符号引用(常量名)替换为直接引用(地址)的过程。

  • 初始化

    • 执行类构造器<clinit>()方法,该方法是有编译期自动收集类中所有变量的赋值动作和静态代码中的语句并产出。

    • 父类没有进行初始化,则先初始化父类。

public class TestInit {
public static void main(String[] args) {
Son s = new Son();
System.out.println(Son.m);
}
}

class Father {
public Father(){
System.out.println("这是父类");
}

}

class Son extends Father{
static {
System.out.println("这是子类的静态代码块");
m = 300;
}
static int m = 100;
public Son(){
System.out.println("这是子类");
}
}

什么时候会发生初始化

  • 主动引用

    • 先初始化main方法所在的类

    • new

    • 调用类的静态成员(除了final)和静态方法

    • 反射

    • 父类没有被初始化,则先初始化

  • 被动引用

    • 访问静态域,如通过子类引用父类的静态变量。

    • 通过数组定义

    • 引用常量

public class TestInit_02 {
//先执行
static {
System.out.println("这是main"); //1
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用
//new
// SonTest s = new SonTest();
//调用类的静态成员
// System.out.println(SonTest.k);
//反射
// Class.forName("SonTest");
//被动引用
System.out.println(SonTest.m);
}
}

class FatherTest{
static int m = 200;
public FatherTest(){
System.out.println("这是父类");//3
}
}

class SonTest extends FatherTest{
static {
System.out.println("这是子类的静态代码块");//2
}
static int k = 100;
public SonTest(){
System.out.println("这是子类");//4
}
}

类加载器的作用

  • 将class文件字节码内容加载到内存中,并将这些静态数据转换程方法区运行时的数据结构,生成一个class对象。

  • 类缓存:一旦某个类加载到类加载器中,它将维持缓存一段时间,JVM会自动回收这些对象。

  • JVM定义了如下类的加载器

    • 引导类加载器:C++编写的,负责java平台核心库

    • 扩展类加载器:加载jre/lib/ext

    • 系统类加载器:加载java-classpath或java.class.path

public class TestLoader {
public static void main(String[] args) {
//系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//引导类加载器(由于是c++编写,因此无法获取)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//自己写的类是由哪个加载器加载的
User user = new User();
ClassLoader classLoader = user.getClass().getClassLoader();
System.out.println(classLoader);
//加载器是加载哪些路径
//F:\JDK\jdk-1.8\jre\lib\charsets.jar;
// F:\JDK\jdk-1.8\jre\lib\deploy.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\access-bridge-64.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\cldrdata.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\dnsns.jar;
// :\JDK\jdk-1.8\jre\lib\ext\jaccess.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\jfxrt.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\localedata.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\nashorn.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\sunec.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\sunjce_provider.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\sunmscapi.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\sunpkcs11.jar;
// F:\JDK\jdk-1.8\jre\lib\ext\zipfs.jar;
// F:\JDK\jdk-1.8\jre\lib\javaws.jar;
// F:\JDK\jdk-1.8\jre\lib\jce.jar;
// F:\JDK\jdk-1.8\jre\lib\jfr.jar;
// F:\JDK\jdk-1.8\jre\lib\jfxswt.jar;
// F:\JDK\jdk-1.8\jre\lib\jsse.jar;
// F:\JDK\jdk-1.8\jre\lib\management-agent.jar;
// F:\JDK\jdk-1.8\jre\lib\plugin.jar;
// F:\JDK\jdk-1.8\jre\lib\resources.jar;
// F:\JDK\jdk-1.8\jre\lib\rt.jar;
// F:\idea-file\基础\注解\target\classes;
// E:\idea2020.1\IDEA-2020.1\IntelliJ IDEA 2020.1\lib\idea_rt.jar
String property = System.getProperty("java.class.path");
System.out.println(property);
}
}