1. 什么是注解
- Annotation是从JDK5.0开始引入的新技术
- Annotation的作用:
- 不是程序本身,可以对程序作出解释(这一点和注释没区别)
- 可以被其他程序(比如:编译器等)读取
- Annotation的格式:
- 注解是以 @注释名 在代码中存在的,还可以添加一些参数值,例如:@注释名(value=“值”)
- Annotation在哪里使用?
- 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程来实现对这些元数据的访问
2. 元注解
元注解的作用:负责注解其他注解,Java定义了4个标准的meta-annotion类型,他们被用来提供对其他annotation类型作说明,这些类型和它们所支持的类在java.lang.annotation包中可以找到
- @Target:用于描述注解的使用范围(被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
- (SOURCE < CLASS < RUNTIME)
- 一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解;
如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,使用SOURCE 注解。
- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
3. 自定义注解
使用**@interface**自定义注解时,自动继承了java.lang.annotation.Annotataion接口
- @interface用来声明一个注解,格式:
public @interface 注解名 {定义内容}
- 其中的每一个方法,实际上是声明了一个配置参数
- 方法名称就是参数名称
- 返回值类型就是参数类型
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,定义注解元素时,经常使用空字符串,0 作为默认值
import java.lang.annotation.*;
public class MyAnnotation {
//作用在方法上,如果没有设置默认值,就必须赋值
@MyAnnotation2(name = "awsl")
public void test(){
}
}
//作用在类上(ElementType),或者方法上(ElementType.METHOD)
@Target({ElementType.TYPE,ElementType.METHOD})
//注解的生命周期为RUNTIME
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation2{
//这个方法一样的就是注解中的参数,default是设置默认值
String name() default "";
}
4.反射机制(Reflection)
-
Reflection是java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c = Class.forName("java.lang.String")
-
加载完类之后, 在堆内存的方法区中就产生了一个Class对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构
-
将一个类的内部结构,一个一个都封装成一个对象(所有方法是一个对象,所有变量是一个对象,所有的构造器是一个对象)
-
可以解耦,提高程序的可扩展性
-
正常方式:
- 引入需要的“包类”名称
- 通过new实例化
- 取得实例对象
-
反射方式:
- 实例化对象
- . getClass()方法
- 得到完整的“包类”名称
5.Class类
(这篇有对Class类有详细解释)https://blog.csdn.net/mcryeasy/article/details/52344729
每一个类都有一个Class对象,每当编译一个新类就产生一个Class对象,基本类型 (boolean, byte, char, short, int, long, float, and double)有Class对象,数组有Class对象,就连关键字void也有Class对象(void.class)。Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。相同类型和同一维度的实例对象的Class对象都是同一个。
5.1 获取Class类的实例的方式
- 若已知具体的类,通过类的class属性获取,该方式最为安全可靠,程序性能最高
Class clazz = Person.class;
- 已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = person.geiClass();
- 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取(将字节码文件加载到内存),可能会抛出ClassNotFoundException
Class clazz = class.forName("demo.Student")
- 内置基本数据类型可以直接用 类名.Type
- 利用ClassLoader
5.2 Class类的方法
方法名 | 说明 |
---|---|
forName() | (1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。(2)为了产生Class引用,forName()立即就进行了初始化。 |
Object-getClass() | 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。 |
getName() | 取全限定的类名(包括包名),即类的完整名字。 |
getSimpleName() | 获取类名(不包括包名) |
getCanonicalName() | 获取全限定的类名(包括包名) |
isInterface() | 判断Class对象是否是表示一个接口 |
getInterfaces() | 返回Class对象数组,表示Class对象所引用的类所实现的所有接口。 |
getSupercalss() | 返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。 |
newInstance() | 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 |
getFields() | 获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。 |
getDeclaredFields | 获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。 |
getConstructors() |
5.2.1 Field:获取成员变量
getField(String name)
获取指定名称的公共的成员变量,返回一个Field对象
getFields()
获取这个class对象所有的公共的成员变量,返回一个Field对象
getDeclaredField(String name)
获取指定成员变量,包括私有的,并返回一个Field对象
Field.get(Object obj)
获取实例对象的成员变量的值,需要传入一个实例对象参数
import java.lang.reflect.Field;
public class TestGetClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Person person = new Person();
// 三种获取class对象的方式
Class c1 = Person.class;
System.out.println(c1.hashCode());
Class c2 = Class.forName("Demo2.Person");
System.out.println(c2.hashCode());
Class c3 = person.getClass();
System.out.println(c3.hashCode());
//通过.getFields()获取所有 public 成员变量,并返回一个Field对象数组
//注意,这时候的.getClass() 获取的不是person对象,是一个class对象
Field[] field1 = person.getClass().getFields();
for (Field fields : field1) {
//通过.get()获取这个成员变量的值
System.out.println(fields.get(person));
}
//======================================================================
//通过.getDeclaredField()获取指定成员变量,包括 private ,并返回一个Field对象
Field field2 = person.getClass().getDeclaredField("b");
//如果获取了私有变量的值,虽然会拿到值,但是会报修饰符安全检查的异常
// .setAccessible(true)这里只需要设置忽略异常就可以
field2.setAccessible(true);
System.out.println(field2.get(person));
}
}
class Person {
public int a;
private int b;
public void test1() {
}
}
5.2.2 Construcor:获取构造器
getConstructor(Class<?>... parameterTypes)
获取无参构造或者有参构造(填上对应的有参参数列表类型),返回一个Constructor对象
Constructor.newInstance(Object ... initargs)
通过构造器获取实例对象,可传入有参构造的值,返回一个object对象
public class TestGetClass {
public static void main(String[] args) throws Exception {
Person person = new Person();
//通过.getConstructor()获取无参构造,或者有参构造,有参构造需要给对应的参数列表,返回一个Constructor对象
Constructor<? extends Person> constructor=person.getClass().getConstructor();
//通过构造器对象获得类对象(注意,这里不是class对象)返回一个object对象
Object object = constructor.newInstance();
System.out.println(object);
//====================================================================
//通过.getConstructor()获取无参构造,或者有参构造,有参构造需要给对应的参数列表,返回一个Constructor对象
Constructor<? extends Person> constructor=person.getClass().getConstructor(int.class,int.class);
//通过构造器对象获得类对象(注意,这里不是class对象)返回一个object对象,可以传入有参构造的值
Object object = constructor.newInstance(10,20);
System.out.println(object);
}
}
class Person {
public int a;
private int b;
public Person() {
}
public Person(int a, int b) {
this.a = a;
this.b = b;
}
public void test1() {
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
@Override
public String toString() {
return "Person{" +
"a=" + a +
", b=" + b +
'}';
}
}
5.2.3 Method:获取方法
getMethods()
获取class对象中的所有方法,返回值为Method对象数组
getMethod( String name, Class<?>... parameterTypes )
获取class对象中指定的方法,第一个是方法名,第二个参数为有参方法的参数列表类型。返回值为Method对象
Method.invoke(Object obj, Object... args)
调用方法,第一个参数为对象实例,第二个参数为方法参数值
public class TestGetClass {
public static void main(String[] args) throws Exception {
Person person = new Person();
//获取这个person类的class对象的所有方法,返回一个Method对象
Method[] method=Person.class.getMethods();
for (Method method1 : method) {
System.out.println(method1.getName());
}
//获取指定的class对象的方法,参数为方法名还有方法的参数列表的类型,返回一个Method对象
Method method1 =Person.class.getMethod("test1",String.class);
System.out.println(method1);
//通过方法对象,调用方法,参数是实例对象和方法的参数
method1.invoke(person,"张三");
}
}
class Person {
public int a;
private int b;
public Person() {
}
public Person(int a, int b) {
this.a = a;
this.b = b;
}
public void test1(String name) {
System.out.println("名字是"+name);
}
}
5.2.4 通过配置文件获取class和method
注意,配置文件要放在resources下,不然可能会找不到资源报空指针
public class TestGetClass {
public static void main(String[] args) throws Exception {
//创建Properties对象
Properties properties = new Properties();
//获取class对象的类加载器
ClassLoader classLoader = Person.class.getClassLoader();
//从类加载器中找到配置文件,返回一个Inputstream
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
//加载配置文件
properties.load(resourceAsStream);
//获取配置文件中的信息
String className =properties.getProperty("className");
String methodName=properties.getProperty("methodName");
Class cls =Class.forName(className);
//创建实例对象
Object obj = cls.newInstance();
//注意自己的方法有没有参数
Method met = cls.getMethod(methodName,String.class);
//调用方法
met.invoke(obj,"张三");
}
}
}
pro.properties
className=Demo2.Person
methodName=test1
6. Java内存
- 堆
- 存放new的对象和数组
- 可以被所有的线程共享,不会存放别的对象引用
- 栈
- 存放基本变量类型(会包含这个基本类型的具体数值)
- 引用对象的变量(会存放这个引用在堆里的具体地址)
- 方法区
- 可以被所有的线程共享
- 包含了所有class的static变量,方法、常量池、代码
7. 类的加载过程
引用自:https://blog.csdn.net/m0_38075425/article/details/81627349
加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。
- 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式。
- 从JAR包加载class文件,这种方式也是很常见的,前面介绍JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。
- 通过网络加载class文件。
- 把一个Java源文件动态编译,并执行加载。
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。