注解
什么是注解?
通俗易懂的来说:注释是给人看的,注解可以给机器看。
注解是从JDK5.0引入的新技术。
内置注解
@Override:定义在java.lang.Override中,此方法只适用于修辞方法。表示一个方法声明打算重写父类中另一个方法。
@Deprecated:定义在java.lang.Deprecated中,表示当前方法已经过时,不建议使用或者已经存在了更好的方法来选择。
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。
元注解
@Target:表明在哪里使用这个注解,类或者方法
@Retention:表明在运行时使用还是在源码范围使用runtime>class>sources
@Documented//表明是否生成Javadoc
@Inherited//表明子类可以继承父类的注解
package com;
import java.lang.annotation.*;
@myAnnotation
public class Test1 {
}
//元注解
@Target(value = {ElementType.TYPE})//表明在方法还是类上使用。。
@Retention(value = RetentionPolicy.RUNTIME)//表明在运行时使用还是源码。。
@Documented//表明是否生成Javadoc
@Inherited//表明子类可以继承父类的注解
@interface myAnnotation{
}
自定义注解
package com;
import java.lang.annotation.*;
@myAnnotation3("自定义")
@myAnnotation2(age = 18)
public class Test2 {
}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation2{
//注解的参数:参数类型+参数名();这里的括号不代表方法!!
String name() default "";
int age();
int id() default -1;
String[] schools() default {"安阳师范学院","软件学院"};
}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation3{
String value();//不成为的规定,如果注解只有一个参数,最好用value进行定义参数名,这样标注时可以省略value
}
反射机制
先了解一下静态动态语言:
动态语言:是一类运行时可以改变结构的语言:例如新的函数、对象、甚至代码可以被引进。主要的动态语言:JavaScript、PHP、Python。
静态语言:与动态语言不同的就是,运行时无法改变自己的结构。例如Java、C、C++。
Java不是动态语言,但是我们可以称为准动态语言,就是因为反射机制的存在,因为反射机制,可以让java编程更具有灵活性,但是也会存在一些性能上的弊端。
获得反射对象
package com;
import lombok.Data;
//反射
public class reflection {
public static void main(String[] args) throws ClassNotFoundException {
Class a = Class.forName("com.User");
Class b = Class.forName("com.User");
Class c = Class.forName("com.User");
Class d = Class.forName("com.User");
System.out.println(a);
//这里的hashcode都是一个值,代表相同的对象
System.out.println(b.hashCode());
System.out.println(c.hashCode());
System.out.println(d.hashCode());
}
}
@Data
class User{
private String name;
private int age;
private String password;
}
Class类的创建方式
第一个阶段:源代码阶段,就是我们自己写的java文件和编译生成的.class字节码文件。
第二阶段:是字节码文件被类加载器加载后的文件。是Class类对象阶段。
第三阶段:就是运行时阶段,这时候我们可以根据创建好的对象获取Class对象。
三种方式获取Class对象:(对应上述三种阶段)
-
class.forName("全类名")
*多用于配置文件,因为括号中写字符串
-
类名.class
*多用于参数的传递
-
对象.getClass()
*多用于对象获取字节码方式
结论:同一个字节码文件(*.class)在程序运行的过程中,只会被加载一次,无论使用哪一种方式获取的Class对象都是一样的。
package com;
import lombok.Data;
public class Test3 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
//方式一:通过对象获取,运行时获取
Class c1 = person.getClass();
System.out.println(c1);
//方式二:forName获得,源代码阶段获取,静态方法
Class c2 = Class.forName("com.Student");
System.out.println(c2);
//方式三:通过类名.class获取,类加载器获取
Class c3 = Student.class;
System.out.println(c3);
//方式四:基本内置类型的包装类都有一个TYPE属性
Class c4 = Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
@Data
class Person{
public String name;
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
class Teacher extends Person{
public Teacher() {
this.name = "老师";
}
}
所有类型的Class对象
package com;
import java.lang.annotation.ElementType;
public class AllClass {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Integer.class; //整型
Class c3 = Comparable.class; //接口
Class c4 = int[].class; //一维数组
Class c5 = int[][].class;//二维数组
Class c6= Override.class;//注解
Class c7 = void.class;//void
Class c8 = Class.class;//Class
Class c9 = ElementType.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);
}
}
Java内存分析
方法区相当于一个特殊的堆。
听课的个人理解(不知道对不对):
首先加载内存,把各种类的数据转换成Class对象,存入堆中。然后进行链接,链接结束m=0,最后clinit方法从堆中取出来需要的类,合并代码。
反射对象Class对象功能:
以下从jdk文档中查到:
获取成员变量
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Field;
//获取成员变量
public class test1 {
public static void main(String[] args) throws NoSuchFieldException {
User a = new User();
Class c1 = a.getClass();
//获取public修饰的成员变量
Field[] fields = c1.getFields();
for (Field f:fields){
System.out.println(f);
}
System.out.println("=======================");
//获取指定名称的public修饰的成员变量
Field f = c1.getField("age");
System.out.println(f);
System.out.println("=======================");
//获取所有的成员变量
Field[] fields1 = c1.getDeclaredFields();
for (Field fList:fields1){
System.out.println(fList);
}
System.out.println("=======================");
Field field = c1.getDeclaredField("sex");
System.out.println(field);
}
}
获取构造方法们
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class test2 {
public static void main(String[] args) throws Exception{
User u = new User();
Class classUser = u.getClass();
//查询到public修饰的所有构造方法
System.out.println("========查询到public修饰的所有构造方法==========");
Constructor[] c1s = classUser.getConstructors();
for (Constructor constructor:c1s){
System.out.println(constructor);
}
//括号内如果不写就是获取无参,带参的就是获取带参,并且参数顺序要与原来位置一样
System.out.println("========查询带参的构造方法==========");
Constructor c2 = classUser.getConstructor();
System.out.println(c2);
System.out.println("========查询不带参的构造方法==========");
Constructor c3 = classUser.getConstructor(String.class,int.class);
System.out.println(c3);
//我们可以获取带参的构造方法后进行赋值
User user1 = (User)c3.newInstance("小明",18);
System.out.println(user1);
System.out.println("========查询不带参的构造方法==========");
//对于一般的无参构造函数,我们都不会先获取无参构造器之后在进行初始化。而是直接调用Class类内的newInstance()方法
Object user3 = classUser.newInstance();
System.out.println(user3);
}
}
获取成员方法们
Method[] getMethods()
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Method;
public class test3 {
public static void main(String[] args) throws Exception{
Class userClass = User.class;
Method[] methods = userClass.getMethods();
System.out.println("============查询public修饰的方法===============");
for (Method m:methods){
System.out.println(m);//查询出来很多的原因是我们的user类归属object类,所以这里有很多其他的
}
System.out.println("============查询public修饰带参的方法===============");
Method method = userClass.getMethod("sleep");
System.out.println(method);
User u = new User("小明",1);
method.invoke(u);//执行方法,放入对象。
Method method2 = userClass.getMethod("sleep",String.class);
System.out.println(method2);
method2.invoke(u,"小红");//注意这里多个参数时,这里要传值
Field field = userClass.getDeclaredField("name");
field.setAccessible(true);//不能直接操作私有属性,我们先关闭安全监测
field.set(u,"明明");
System.out.println(u.getName());
}
}
获取简单方法、简单类名
String getName()
package com.ClassForMethod;
import com.pojo.User;
import java.lang.reflect.Method;
public class test04 {
public static void main(String[] args) {
Class user = User.class;
Method[] methods = user.getMethods();
for (Method m:methods){
String easy = m.getName();
System.out.println(easy);
}
}
}
注意:我们除了上面说可以用Declared修饰的方法获取所有的方法名、成员变量名。。。还可以使用暴力反射来获取。
xxx.setAccessible(true);
类加载器:
类加载器的作用:是把类(class)装载进内存,JVM规范定义了三种类型的类加载器。
引导类加载器:用C++编写,是JVM自带类的加载器,负责Java平台核心库。用来装载类库,这个加载器无法直接获取。
扩展类加载器:负责jre/lib/text目录下的jar包或者-D java.ext.dirs指定目录下的jar包装入工作库。
系统类加载器:负责java -classpath或者-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。
package com;
public class classLoader {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemLoader);
//获取系统类加载器的父类加载器->扩展类加载器
ClassLoader parent = systemLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器->根加载器(C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是哪一个加载器加载的
ClassLoader classLoader = Class.forName("com.classLoader").getClassLoader();
System.out.println(classLoader);
//测试jdk内置是哪一个加载器加载的
ClassLoader sysClassLoder = Class.forName("java.lang.Object").getClassLoader();
System.out.println(sysClassLoder);
}
}
调用方法的三种性能检测:
package com;
import java.lang.reflect.Method;
public class performance {
public static void main(String[] args) throws Exception {
//性能测试,关闭了安全监测
test01();
test02();
test03();
}
public static void test01(){
//普通方法调用
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
user.getName();
}
long end = System.currentTimeMillis();
System.out.println(end-start+"ms");
}
public static void test02() throws Exception {
//反射调用
User user = new User();
Class userClass = User.class;
Method getName = userClass.getMethod("getName",null);
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println(end-start+"ms");
}
public static void test03() throws Exception {
//反射调用,关闭安全监测
User user = new User();
Class userClass = User.class;
Method getName = userClass.getMethod("getName",null);
long start = System.currentTimeMillis();
getName.setAccessible(true);
for (int i = 0; i < 100000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println(end-start+"ms");
}
}