注解(Annotation)和注释(Comment)很类似,只不过注释是给程序员看的,帮助程序员理解程序代表的意思和逻辑,而注解既是给程序员看的,又是给程序看的,如编译器,程序可以对不同的注解做出与之相应的动作。
@Override:修饰方法,表示该方法是对父类方法的重写,编译器会检查该方法的定义方式,保证和父类的方法相同。
@Deprecated:修饰属性,方法,类,表示这些东西已经过时了,有更好的替代方式或者很危险,虽然不推荐但还是可以编译并执行。
@SuppressWarnings:用于抑制编译时的警告信息,后面可以接一些参数:
比如:
@SuppressWarnings(“all”)
@SuppressWarnings(“unchecked”)
@SuppressWarnings({value=“unchecked”,"deprecation“})
//无参数的 @Override public void test(){} //有参数的 @SuppressWarnings("all") class Test{ int i; ... }
元注解(meta-anotation)就是对注解进行注解,为注解提供一些说明。
比如:
@Target:描述注解的使用范围
需要的参数:
ElementType.TYPE、ElementType.FIELD、ElementType.METHOD、ElementType.PARAMETER等等。
@Retention:描述注解的生命周期。编译时存在,还是类加载存在,还是运行时存在。(SOURCE<CLASS<RUNTIME)
需要的参数:
RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME
@Document:说明该注解将包含在javadoc中。
@Inherited:说明子类可以继承父类的该注解。
//注解的使用 @MyAnnotation01(name = "ace") public class Test01{ } //元注解 @Target({ElementType.METHOD,ElementType.TYPE}) //表示我们的注解只能用在方法、类型上,类也是一个类型。 @Retention(RetentionPolicy.RUNTIME) //表示我们的注解运行时任然有效,通常都是使用这个。 //自定义注解 @interface MyAnnotation01{ //注解的参数,参数的数据类型只能是基本数据类型,Class,String,Enum String name(); //参数可以有默认值 int sex() default 1; }
//注解的使用 @MyAnnotation02({1,2,3}) public class Test01{ } //元注解 @Target({ElementType.METHOD,ElementType.TYPE}) //表示我们的注解只能用在方法、类型上,类也是一个类型。 @Retention(RetentionPolicy.RUNTIME) //表示我们的注解运行时任然有效,通常都是使用这个。 //自定义注解 @interface MyAnnotation02{ //当参数只有一个的时候,推荐使用value做参数名,这样使用的时候可以省略参数名。 int[] value(); }
反射(Reflection)就如同照镜子一样,照镜子能得到我们清晰、完整的模样,反射机制能得到一个类的完整结构,我们就可以操作任意该类对象的所有属性和方法。
a. 我们通过反射机制获得的是一个Class类对象,该类保存了一个类的完整结构。
b. Class在类被加载的时候创建,且一个类只有一个Class对象,所有类实例的Class对象都是相同的。
注意是Class,而不是class,小写的class是关键字
public class Test { public static void main(String[] args) { Class c1 = Object.class; //类 Class c2 = Comparable.class; //接口 Class c3 = int[].class; //一维数组 Class c4 = String[][].class; //二维数组 Class c5 = Override.class; //注解 Class c6 = ElementType.class; //枚举 Class c7 = Integer.class; //基本数据类型 Class c8 = void.class; //void Class c9 = Class.class; //Class } }
public class Test02 { public static void main(String[] args) throws ClassNotFoundException { //通过类的实例获取该类的Class实例 A a = new A(); Class classA01 = a.getClass(); System.out.println(classA01.hashCode()); //若知道类名,则直接通过类属性class获取该类的Class实例 Class classA02 = A.class; System.out.println(classA02.hashCode()); //若知道类的全名(包名+类名)则可以通过Class的类方法forName获取该类的Class实例 Class classA03 = Class.forName("com.ace.test.A"); System.out.println(classA03.hashCode()); //基本数据类型的包装类的Class实例的获取可以通过包装类中的TYPE属性获取 Class classInt = Integer.class; System.out.println(classInt.hashCode()); //通过子类的Class实例获取父类的Class实例 Class classB = classA01.getSuperclass(); } } class A extends B{ private int a; private void test(){ } } class B { int b; }
//获取类的信息 public class Test06 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class class01 = Class.forName("com.ace.test.Person"); //获取类的名字 System.out.println(class01.getName()); //包名 + 类名 com.ace.test.Person System.out.println(class01.getSimpleName()); //类名 Person //获取类的属性 Field[] fields01 = class01.getFields(); //只能得到public,其他权限都不可以 Field[] fields02 = class01.getDeclaredFields(); //得到全部权限的属性 //获得指定的属性 Field field01 = class01.getField("id"); Field field02 = class01.getDeclaredField("age"); //获得方法 Method[] methods01 = class01.getMethods(); Method[] methods02 = class01.getDeclaredMethods(); //获得指定的方法,需要传入方法名和形参类型的Class实例对象 Method method01 = class01.getMethod("test01", int.class, int.class); Method method02 = class01.getDeclaredMethod("test02",null); //无参数可以填null //获得构造器 Constructor[] constructors = class01.getConstructors(); Constructor[] constructors1 = class01.getDeclaredConstructors(); //获得指定的构造器 Constructor constructor = class01.getConstructor(null); Constructor constructor1 = class01.getDeclaredConstructor(int.class, String.class); } } class Person{ public int id; private String name; protected int age; public Person() { } private Person(int id, String name) { this.id = id; this.name = name; } private void test01(int x, int y){ } void test02(){ } }
public class Test07 { //通过反射动态的创建对象 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class classUser = Class.forName("com.ace.test.User"); //调用newInstance()方法,相当于调用了类的无参构造器 User user1 = (User) classUser.newInstance(); //通过反射机制获取本类构造方法,用这个方法去创建对象 Constructor constructorUser = classUser.getDeclaredConstructor(String.class, String.class); User user2 = (User) constructorUser.newInstance("ace123","123"); //调用普通的方法 Method method1 = classUser.getDeclaredMethod("test02",null); //当调用的方法是私有的时候,需要关闭程序的访问安全检查,Method、Constructor、Field都有这个方法,关闭安全检查可以提高 程序的运行效率 method1.setAccessible(true); method1.invoke(user1,null); //将该方法授权给user1对象使用,并传入参数的Class对象 //操作属性 Field field = classUser.getDeclaredField("name"); field.setAccessible(true); field.set(user2,"ace"); //设置属性的值 String userName = (String) field.get(user2); //获得属性的值 System.out.println(userName); } } class User{ private String name; private String passWord; public User() { } public User(String name, String passWord) { this.name = name; this.passWord = passWord; } public void test01(){ System.out.println("调用了test01()"); } private void test02(){ System.out.println("调用了test02()"); } }
Java采用泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据安全性和免去强制类型转换问题,但是一旦编译完成,所有和泛型相关的信息全部擦除。
为了通过反射操作这些类型,Java新增了几种类型来代表不能归到Class类中的类型但是又和原始类型齐名的类型。
- ParameterizedType:表示一种参数化的类型。
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的类型。
- TypeVarible:各种类型变量的公共父接口。
- WildcardType:表示一种通配符类型表达式。
public class Test08 { public Set<Integer> test01(Map<Integer,String> map, List<String> list){ System.out.println("调用了test01()"); return null; } public static void main(String[] args) throws NoSuchMethodException { Method method = Test08.class.getMethod("test01", Map.class, List.class); //获得参数类型 Type[] genericParameterTypes = method.getGenericParameterTypes(); /*for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); 输出: java.util.Map<java.lang.Integer, java.lang.String> java.util.List<java.lang.String> }*/ for (Type genericParameterType : genericParameterTypes) { if (genericParameterType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); /* 输出: class java.lang.Integer class java.lang.String class java.lang.String */ } } } //获得返回值类型 Type genericReturnType = method.getGenericReturnType(); System.out.println(genericReturnType); //输出java.util.Set<java.lang.Integer> if(genericReturnType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); //输出class java.lang.Intege } } } }
什么是对象映射关系?
public class Test09 { public static void main(String[] args) throws ClassNotFoundException { Class c = Class.forName("com.ace.test.Student"); //直接通过反射获得注解,只能获得类外部的注解 Annotation[] annotations = c.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); //@com.ace.test.TableAnno(value=t_student) } //指定注解的类型,获得注解的参数值,也只能获取到类外部的注解 TableAnno TableAnno = (TableAnno) c.getAnnotation(TableAnno.class); //获取注解的参数值 String value = TableAnno.value(); System.out.println(value); //t_student //获取类内部的注解 //先获取到被注解的元素,如Field、Method等等,再获取该元素的某个注解,就可以获取到注解的参数值了 Field[] Fields = c.getDeclaredFields(); ColumnAnno columnAnno; String name , type; int length; for (Field field : Fields) { //获取所有属性指定的注解的参数值 columnAnno = field.getAnnotation(ColumnAnno.class); //指定获取该属性的哪个注解 name = columnAnno.columnName(); type = columnAnno.type(); length = columnAnno.length(); System.out.println(name + type + length); /* 学号int10 姓名varchar255 年龄int10 */ } } } @TableAnno("t_student") class Student{ @ColumnAnno(columnName = "学号",type = "int",length = 10) int num; @ColumnAnno(columnName = "姓名",type = "varchar",length = 255) String name; @ColumnAnno(columnName = "年龄",type = "int",length = 10) int age; } //类名的注解,类名和表名对应 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface TableAnno{ String value(); } //属性的注解,属性和字段对应 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @interface ColumnAnno{ String columnName(); //列名 String type(); //字段类型 int length(); //字段长度 }
深入理解JVM—JVM内存模型
I. 加载:将类的字节码文件.class读入内存,并为之创建一个Class对象,此过程由类加载器ClassLoader完成。
II. 链接:验证(保证安全)–>准备(为静态变量分配内存并为其设置默认值)–>解析(将虚拟机常量池中的符号引用替换为直接 引用)
II. 初始化:执行类构造器<clinit>()方法。如果其父类还未初始化,则触发父类的初始化。
注意:类构造器是构造类信息的,不是构造该类对象的构造器,是编译器在编译阶段自动收集类中的类变量赋值语句,和静态代 码块中的语句合成而来的。
public class Test03 { public static void main(String[] args) { C c = new C(); System.out.println(C.m); /** * 初始化过程: * 1.加载:加载到内存,产生一个Class对象。 * 2.链接:链接结束后,m = 0; * 3.初始化: * 相当于 * <clinit>(){ * System.out.println("C类静态代码块初始化"); * m =10; * m=30 * } * 所以最后m = 30 */ } } class C{ static { System.out.println("C类静态代码块初始化"); m =10; } static int m = 30; public C() { System.out.println("C类无参构造初始化"); } }
I. main方法所在类
II. new一个类的对象
III. 调用类的静态属性(static final常量除外)和静态方法
VI. 对类进行反射
V. 当初始化一个类的时候,若父类未初始化,子类会先初始化父类。
I. 通过数组定义类引用
II. 调用类中的常量,因为常量在链接阶段就存入调用类中的常量池中了。
III. 调用静态属性和静态方法的时候,只有真正声明这个静态成员的类才会被初始化。比如通过子类使用父类的静态属性,子类不会初始化。
public class Test04 { static { System.out.println("main方法所在类被加载!"); } public static void main(String[] args) { //一定会初始化,一个类只会被加载一次 Father father = new Father(); Class c1 = Father.class; //不会初始化 Son[] sons = new Son[5]; System.out.println(Son.a); } } class Father{ static int a = 5; static { System.out.println("父类被加载!"); } } class Son extends Father{ static { System.out.println("子类被加载!"); } } /** * 结果: * main方法所在类被加载! * 父类被加载! * 5 */
引导类加载器(BootstapClassLoader):负责加载Java核心类库。该加载器无法直接获取。
扩展类加载器(ExtensionClassLoader或者ExtClassLoader):负责jre/lib/ext目录下的jar包。
系统类加载器(SystemClassLoader或者AppClassLoader):负责java -classpath或-D java.class.path所指目录下的jar包,是最常见的类加载器。
public class Test05 { public static void main(String[] args) throws ClassNotFoundException { //获取系统类加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //获取系统类加载器的父类加载器-->扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //获取扩展类加载器的父类加载器-->根类加载器 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); //获取不到 //测试当前类是由哪个类加载器加载的 ClassLoader classLoader3 = Class.forName("com.ace.test.Test05").getClassLoader(); System.out.println(classLoader3); //测试jdk的内置类是由哪个类加载器加载的 ClassLoader classLoader4 = Class.forName("java.sql.Connection").getClassLoader(); System.out.println(classLoader4); //根加载器,获取不到 //获取系统加载器可以加载到的路径 System.out.println(System.getProperty("java.class.path")); } }
java.lang.ClassLoader中的loadClass方法源码:
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
示意图:
这样设计的好处?
如果有人想替换系统级别的类:String、Object等等。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap ClassLoader加载过了,因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader,所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。