注解
什么是注解?
通俗易懂的來(lái)說(shuō):注釋是給人看的,注解可以給機(jī)器看。
注解是從JDK5.0引入的新技術(shù)。
內(nèi)置注解
@Override:定義在java.lang.Override中,此方法只適用于修辭方法。表示一個(gè)方法聲明打算重寫父類中另一個(gè)方法。
@Deprecated:定義在java.lang.Deprecated中,表示當(dāng)前方法已經(jīng)過(guò)時(shí),不建議使用或者已經(jīng)存在了更好的方法來(lái)選擇。
@SuppressWarnings:定義在java.lang.SuppressWarnings中,用來(lái)抑制編譯時(shí)的警告信息。
元注解
@Target:表明在哪里使用這個(gè)注解,類或者方法
@Retention:表明在運(yùn)行時(shí)使用還是在源碼范圍使用runtime>class>sources
@Documented//表明是否生成Javadoc
@Inherited//表明子類可以繼承父類的注解
package com;
import java.lang.annotation.*;
@myAnnotation
public class Test1 {
}
//元注解
@Target(value = {ElementType.TYPE})//表明在方法還是類上使用。。
@Retention(value = RetentionPolicy.RUNTIME)//表明在運(yùn)行時(shí)使用還是源碼。。
@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{
//注解的參數(shù):參數(shù)類型+參數(shù)名();這里的括號(hào)不代表方法?。? String name() default "";
int age();
int id() default -1;
String[] schools() default {"安陽(yáng)師范學(xué)院","軟件學(xué)院"};
}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotation3{
String value();//不成為的規(guī)定,如果注解只有一個(gè)參數(shù),最好用value進(jìn)行定義參數(shù)名,這樣標(biāo)注時(shí)可以省略value
}
反射機(jī)制
先了解一下靜態(tài)動(dòng)態(tài)語(yǔ)言:
動(dòng)態(tài)語(yǔ)言:是一類運(yùn)行時(shí)可以改變結(jié)構(gòu)的語(yǔ)言:例如新的函數(shù)、對(duì)象、甚至代碼可以被引進(jìn)。主要的動(dòng)態(tài)語(yǔ)言:JavaScript、PHP、Python。
靜態(tài)語(yǔ)言:與動(dòng)態(tài)語(yǔ)言不同的就是,運(yùn)行時(shí)無(wú)法改變自己的結(jié)構(gòu)。例如Java、C、C++。
Java不是動(dòng)態(tài)語(yǔ)言,但是我們可以稱為準(zhǔn)動(dòng)態(tài)語(yǔ)言,就是因?yàn)榉瓷錂C(jī)制的存在,因?yàn)榉瓷錂C(jī)制,可以讓java編程更具有靈活性,但是也會(huì)存在一些性能上的弊端。

獲得反射對(duì)象
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都是一個(gè)值,代表相同的對(duì)象
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類的創(chuàng)建方式

第一個(gè)階段:源代碼階段,就是我們自己寫的java文件和編譯生成的.class字節(jié)碼文件。
第二階段:是字節(jié)碼文件被類加載器加載后的文件。是Class類對(duì)象階段。
第三階段:就是運(yùn)行時(shí)階段,這時(shí)候我們可以根據(jù)創(chuàng)建好的對(duì)象獲取Class對(duì)象。
三種方式獲取Class對(duì)象:(對(duì)應(yīng)上述三種階段)
-
class.forName("全類名")
*多用于配置文件,因?yàn)槔ㄌ?hào)中寫字符串
-
類名.class
*多用于參數(shù)的傳遞
-
對(duì)象.getClass()
*多用于對(duì)象獲取字節(jié)碼方式
結(jié)論:同一個(gè)字節(jié)碼文件(*.class)在程序運(yùn)行的過(guò)程中,只會(huì)被加載一次,無(wú)論使用哪一種方式獲取的Class對(duì)象都是一樣的。
package com;
import lombok.Data;
public class Test3 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
//方式一:通過(guò)對(duì)象獲取,運(yùn)行時(shí)獲取
Class c1 = person.getClass();
System.out.println(c1);
//方式二:forName獲得,源代碼階段獲取,靜態(tài)方法
Class c2 = Class.forName("com.Student");
System.out.println(c2);
//方式三:通過(guò)類名.class獲取,類加載器獲取
Class c3 = Student.class;
System.out.println(c3);
//方式四:基本內(nèi)置類型的包裝類都有一個(gè)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 = "學(xué)生";
}
}
class Teacher extends Person{
public Teacher() {
this.name = "老師";
}
}
所有類型的Class對(duì)象
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; //一維數(shù)組
Class c5 = int[][].class;//二維數(shù)組
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內(nèi)存分析

方法區(qū)相當(dāng)于一個(gè)特殊的堆。


聽課的個(gè)人理解(不知道對(duì)不對(duì)):
首先加載內(nèi)存,把各種類的數(shù)據(jù)轉(zhuǎn)換成Class對(duì)象,存入堆中。然后進(jìn)行鏈接,鏈接結(jié)束m=0,最后clinit方法從堆中取出來(lái)需要的類,合并代碼。
反射對(duì)象Class對(duì)象功能:
以下從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);
}
}
獲取構(gòu)造方法們
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修飾的所有構(gòu)造方法
System.out.println("========查詢到public修飾的所有構(gòu)造方法==========");
Constructor[] c1s = classUser.getConstructors();
for (Constructor constructor:c1s){
System.out.println(constructor);
}
//括號(hào)內(nèi)如果不寫就是獲取無(wú)參,帶參的就是獲取帶參,并且參數(shù)順序要與原來(lái)位置一樣
System.out.println("========查詢帶參的構(gòu)造方法==========");
Constructor c2 = classUser.getConstructor();
System.out.println(c2);
System.out.println("========查詢不帶參的構(gòu)造方法==========");
Constructor c3 = classUser.getConstructor(String.class,int.class);
System.out.println(c3);
//我們可以獲取帶參的構(gòu)造方法后進(jìn)行賦值
User user1 = (User)c3.newInstance("小明",18);
System.out.println(user1);
System.out.println("========查詢不帶參的構(gòu)造方法==========");
//對(duì)于一般的無(wú)參構(gòu)造函數(shù),我們都不會(huì)先獲取無(wú)參構(gòu)造器之后在進(jìn)行初始化。而是直接調(diào)用Class類內(nèi)的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);//查詢出來(lái)很多的原因是我們的user類歸屬object類,所以這里有很多其他的
}
System.out.println("============查詢public修飾帶參的方法===============");
Method method = userClass.getMethod("sleep");
System.out.println(method);
User u = new User("小明",1);
method.invoke(u);//執(zhí)行方法,放入對(duì)象。
Method method2 = userClass.getMethod("sleep",String.class);
System.out.println(method2);
method2.invoke(u,"小紅");//注意這里多個(gè)參數(shù)時(shí),這里要傳值
Field field = userClass.getDeclaredField("name");
field.setAccessible(true);//不能直接操作私有屬性,我們先關(guān)閉安全監(jiān)測(cè)
field.set(u,"明明");
System.out.println(u.getName());
}
}
獲取簡(jiǎn)單方法、簡(jiǎn)單類名
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);
}
}
}
注意:我們除了上面說(shuō)可以用Declared修飾的方法獲取所有的方法名、成員變量名。。。還可以使用暴力反射來(lái)獲取。
xxx.setAccessible(true);
類加載器:
類加載器的作用:是把類(class)裝載進(jìn)內(nèi)存,JVM規(guī)范定義了三種類型的類加載器。
引導(dǎo)類加載器:用C++編寫,是JVM自帶類的加載器,負(fù)責(zé)Java平臺(tái)核心庫(kù)。用來(lái)裝載類庫(kù),這個(gè)加載器無(wú)法直接獲取。
擴(kuò)展類加載器:負(fù)責(zé)jre/lib/text目錄下的jar包或者-D java.ext.dirs指定目錄下的jar包裝入工作庫(kù)。
系統(tǒng)類加載器:負(fù)責(zé)java -classpath或者-D java.class.path所指的目錄下的類與jar包裝入工作,是最常用的加載器。

package com;
public class classLoader {
public static void main(String[] args) throws ClassNotFoundException {
//獲取系統(tǒng)類的加載器
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemLoader);
//獲取系統(tǒng)類加載器的父類加載器->擴(kuò)展類加載器
ClassLoader parent = systemLoader.getParent();
System.out.println(parent);
//獲取擴(kuò)展類加載器的父類加載器->根加載器(C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//測(cè)試當(dāng)前類是哪一個(gè)加載器加載的
ClassLoader classLoader = Class.forName("com.classLoader").getClassLoader();
System.out.println(classLoader);
//測(cè)試jdk內(nèi)置是哪一個(gè)加載器加載的
ClassLoader sysClassLoder = Class.forName("java.lang.Object").getClassLoader();
System.out.println(sysClassLoder);
}
}
調(diào)用方法的三種性能檢測(cè):
package com;
import java.lang.reflect.Method;
public class performance {
public static void main(String[] args) throws Exception {
//性能測(cè)試,關(guān)閉了安全監(jiān)測(cè)
test01();
test02();
test03();
}
public static void test01(){
//普通方法調(diào)用
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 {
//反射調(diào)用
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 {
//反射調(diào)用,關(guān)閉安全監(jiān)測(cè)
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");
}
}