本地環境設定Ruby
1、動態語言和靜態語言
-
動態語言
在執行時可以改變其結構的語言,例如新的函式、物件、已有的函式可以被刪除或是其他結構上的變化。
-
靜態語言
執行時不可改變結構的語言
-
Java不是動態語言,但Java可以被稱為“準動態語言”,Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性
2、反射概述
-
Java Reflection
-
反射機制允許程式在執行期間藉助於Reflection API取得任何類的內部資訊(類名、類的介面、類的方法、類的屬性等等),並能直接操作任何物件的內部屬性及方法。
-
載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個class物件
-
正常方式:
引入需要的包類名稱 ---> new例項化 ---> 取得例項化物件
-
反射:
例項化物件 ---> getClass()方法 ---> 取得完整的包類名稱
-
3、建立Class類的方式
-
方式一:通過getClass()獲取
Class aClass = student.getClass();
-
方式二:通過forName獲取
Class bClass = Class.forName("com.hmx.pojo.Student");
-
方式三:通過類名.class獲取
Class cClass = Student.class;
-
方式四:通過子類的Class獲取
Class personClass = student.getClass().getSuperclass();
-
方式五:基本內建型別的包裝類通過Type屬性可以獲取Class類
Class typeClass = Integer.TYPE;
4、所有型別的Class物件
-
Class
Class c1 = Class.class;
//輸出結果:class java.lang.Class
System.out.println(c1); -
類
Class c2 = Object.class;
System.out.println(c2); -
介面
Class c3 = Comparable.class;
//輸出結果:interface java.lang.Comparable
System.out.println(c3); -
註解
Class c4 = Override.class;
//輸出結果:interface java.lang.Override
System.out.println(c4); -
基本資料型別
Class c5 = Integer.class;
//輸出結果:class java.lang.Integer
System.out.println(c5); -
一維陣列
Class c6 = String[].class;
//輸出結果:class [Ljava.lang.String;
System.out.println(c6); -
二維陣列
Class c7 = String[][].class;
//輸出結果:class [[Ljava.lang.String;
System.out.println(c7); -
列舉
Class c8 = ElementType.class;
//輸出結果:class java.lang.annotation.ElementType
System.out.println(c8); -
void
Class c9 = void.class;
//輸出結果:void
System.out.println(c9);
5、類載入器
-
類載入器的作用
-
將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後在堆中生成一個代表這個類的 java.lang.Class 物件,作為方法區中類資料的訪問入口
-
-
分類
-
系統類載入器:最常用的載入器,主要負責載入classpath所指定的位置的類或者是jar文件
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
//輸出結果為:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(systemClassLoader); -
擴充套件類載入器:主要載入
%JAVA_HOME%/jre/lib/ext
,此路徑下的所有classes目錄以及java.ext.dirs
系統變數指定的路徑中類庫ClassLoader extClassLoader = systemClassLoader.getParent();
//輸出結果為:sun.misc.Launcher$ExtClassLoader@1540e19d
System.out.println(extClassLoader); -
引導類載入器:C++編寫的,JVM自帶的類載入器,負責Java平臺核心庫,用來裝載核心類庫,該載入器無法直接獲取
ClassLoader parent = extClassLoader.getParent();
//輸出結果為:null
System.out.println(parent);
-
6、類載入記憶體分析
以下面這段程式碼為例講解
public class Test03 {
public static void main(String[] args) {
AAA aaa = new AAA();
System.out.println(AAA.num);
}
}
class AAA{
static {
System.out.println("類AAA的靜態程式碼程式碼塊");
}
static int num = 100;
public AAA() {
System.out.println("類AAA的構造方法");
}
}
-
第一步:載入
-
將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後生成一個代表這個類的java.lang.Class物件
-
-
第二步:連結
-
將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程
-
驗證:確保載入的資訊符合JVM規範,沒有安全方面的問題
-
準備:正式為類變數(static)分配記憶體並設定類變數預設初始值的階段,這些記憶體都將在方法區中進行分配
-
解析:虛擬機器常量池內的符號引用(常量名)替換成直接引用(地址)的過程
-
-
-
第三步:初始化
-
執行類構造器方法<clinit>()的過程,類構造器方法<clinit>()是由編譯期自動收集類中所有類變數的賦值動作和靜態程式碼塊中的語句合併產生的
-
當初始化一個類時,如果發現其父類還沒有初始化,則需要先觸發其父類的初始化
-
虛擬機器會保證一個類的<clinit>()方法在多執行緒中會被正確載入和同步
-
7、類的初始化
-
類的主動引用一定會引起類的初始化
-
當虛擬機器啟動時,先初始main方法所在的類
-
new一個類的物件
-
呼叫類的靜態成員(final除外)和靜態方法
-
使用java.lang.Reflection包下的方法對類進行反射呼叫
-
子類的父類沒有被初始化,那麼初始化子類時,父類也會被初始化
-
-
類的被動引用不會引起類的初始化
-
當訪問一個靜態域時,只有真正宣告這個域的類才會被初始化,例如通過子類訪問父類的靜態屬性,子類不會被初始化
-
通過陣列定義類引用,不會觸發此類的初始化
-
引用常量不會觸發此類的初始化,因為常量在連結階段就存入呼叫類的常量池中了
-
8、獲得類的結構
-
1、獲得類名
-
獲得類名
Class c1 = new User().getClass();
c1.getSimpleName(); -
獲得包名 + 類名
Class c1 = new User().getClass();
c1.getName();
-
-
2、獲得類的屬性
-
獲得類公共的屬性
Class c1 = new User().getClass();
c1.getFields();//Field[] -
獲得類所有的屬性
Class c1 = new User().getClass();
c1.getDeclaredFields();////Field[] -
獲得類指定的公共的屬性
Class c1 = new User().getClass();
c1.getField("id"); -
獲得類指定的屬性
Class c1 = new User().getClass();
c1.getDeclaredField("name");
-
-
3、獲得類的方法
-
獲得類和父類的公有方法
Class c1 = new User().getClass();
c1.getMethods();//Method[] -
獲得類的所有方法
Class c1 = new User().getClass();
c1.getDeclaredMethods();//Method[] -
獲得類的指定的公有方法
Class c1 = new User().getClass();
//無參方法
c1.getMethod("getName",null);
//有參方法
c1.getMethod("setName", String.class); -
獲得類的指定的方法
Class c1 = new User().getClass();
c1.getDeclaredMethod("test", null);
-
-
4、獲得類的構造器
-
獲得共有的構造器
Class c1 = new User().getClass();
c1.getConstructors();//Constructor[] -
獲得所有的構造器
Class c1 = new User().getClass();
c1.getDeclaredConstructors();//Constructor[] -
獲得指定的構造器
Class c1 = new User().getClass();
c1.getConstructor(int.class,String.class, int.class);
c1.getDeclaredConstructor(int.class,String.class,int.class);
-
-
5、獲得泛型資訊
-
引數為泛型
//引數為泛型的方法
public void test01(Map<String,User> userMap, List<User> userList){}
//獲得泛型資訊
Method test01 = Test09.class.getMethod("test01", Map.class, List.class);
//獲得方法的泛型引數型別
Type[] gPTypes = test01.getGenericParameterTypes();
for (Type gPType : gPTypes) {
/*
輸出結果:
java.util.Map<java.lang.String, com.hmx.pojo.User>
java.util.List<com.hmx.pojo.User>
*/
System.out.println(gPType);
//判斷gPType是否屬於ParameterizedType的型別
if (gPType instanceof ParameterizedType){
//獲得泛型引數的實際型別
Type[] aTArgus = ((ParameterizedType) gPType).getActualTypeArguments();
for (Type tArgus : aTArgus) {
System.out.println(tArgus);
}
}
} -
返回結果為泛型
//返回結果為泛型
public List<User> test02(){
return null;
}
//獲得泛型資訊
//獲得方法
Method test02 = Test09.class.getMethod("test02",null);
//獲得泛型返回值型別
Type genericRType = test02.getGenericReturnType();
//判斷genericRType是否屬於ParameterizedType的型別
if (genericRType instanceof ParameterizedType){
//獲得泛型返回值的實際型別
Type[] actualTA = ((ParameterizedType) genericRType).getActualTypeArguments();
for (Type aTA : actualTA) {
System.out.println(aTA);
}
}
-
-
6、獲取註解資訊
//自定義註解
9、通過反射建立物件以及操作物件的屬性和方法
-
建立物件:newInstance()方法
-
預設呼叫的是類的無參構造方法
Class c1 = Class.forName("com.hmx.pojo.User");
User user = (User)c1.newInstance(); -
顯示指定呼叫有參構造方法
Class c1 = Class.forName("com.hmx.pojo.User");
Constructor con = c1.getDeclaredConstructor(int.class, String.class, int.class);
User user = (User)con.newInstance(1, "hmx", 23);
-
-
操作物件的屬性
Class c1 = Class.forName("com.hmx.pojo.User");
//建立user物件
User user = (User)c1.newInstance();
//獲得物件的屬性
Field name = c1.getDeclaredField("name");
//不能直接操作私有屬性,我們需要關閉程式的安全檢測
name.setAccessible(true);
//給屬性賦值
name.set(user,"洪夢霞"); -
操作物件的方法
Class c1 = Class.forName("com.hmx.pojo.User");
User user = (User)c1.newInstance();
//操作無參私有方法
Method test = c1.getDeclaredMethod("test",null);
test.setAccessible(true);
//啟用方法
test.invoke(user);
//操作有參公有方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user,"hmx");
10、效能分析
經過以下程式碼可知:(正常建立物件的效率) 高於 (使用反射建立物件,關閉Java安全檢測的效率 )高於 (使用反射建立物件,不關閉Java安全檢測的效率)
public class Test08 {
public static void main(String[] args) throws Exception {
test01();
test02();
test03();
}
//正常建立物件,訪問10億次物件中方法所用時間:4ms
public static void test01(){
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println((endTime-startTime) + "ms");
}
//使用反射建立物件,訪問10億次物件中方法所用時間:1390ms
public static void test02() throws Exception {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println((endTime-startTime) + "ms");
}
//使用反射建立物件,關閉Java安全檢測,訪問10億次物件中方法所用時間:1017ms
public static void test03() throws Exception {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println((endTime-startTime) + "ms");
}
}