天天看點

JavaSE學習day27(反射&JDK新特性)

27.01_反射(類的加載概述和加載時機)

  • A:類的加載概述
  • 當程式要使用某個類時,如果該類還未被加載到記憶體中,則系統會通過加載、連接配接、初始化三步來實作對這個類進行初始化。
  • 加載
    • 就是指将class檔案讀入記憶體,并為之建立一個Class對象。任何類被使用時系統都會建立一個Class對象。
  • 連接配接
    • 驗證 是否有正确的内部結構,并和其他類協調一緻
    • 準備 負責為類的靜态成員配置設定記憶體,并設定預設初始化值
    • 解析 将類的二進制資料中的符号引用替換為直接引用
  • 初始化
  • B:加載時機
  • 建立類的執行個體
  • 通路類的靜态變量,或者為靜态變量指派
  • 調用類的靜态方法
  • 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
  • 初始化某個類的子類
  • 直接使用java.exe指令來運作某個主類

27.02_反射類加載器的概述和分類)

  • A:類加載器的概述
  • 負責将.class檔案加載到記憶體中,并為之生成對應的Class對象。雖然我們不需要關心類加載機制,但是了解這個機制我們就能更好的了解程式的運作。
  • B:類加載器的分類
  • BootStrap ClassLoader 根類加載器
  • Extension ClassLoader 擴充類加載器
  • System ClassLoader 系統類加載器
  • B:類加載器的作用
  • BootStrap ClassLoader 根類加載器
    • 也被稱為引導類加載器,負責java核心類的加載
    • 比如System,String等,在JDK中JRE的lib目錄下rt.jar檔案中
  • Extension ClassLoader 擴充類加載器
    • 負責JRE的擴充目錄中jar包的加載
    • 在JDK中JRE的lib目錄下ext目錄
  • System ClassLoader 系統類加載器
    • 負責在JVM啟動時加載來自java指令的class檔案,以及classpath環境變量所指定的jar包和類路徑

27.03_反射(反射概述)

  • A:反射概述
  • JAVA反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
  • 對于任意一個對象,都能夠調用它的任意一個方法和屬性;
  • 這種動态擷取的資訊以及動态調用對象的方法的功能稱為java語言的反射機制。
  • 想要解剖一個類,必須先要擷取到該類的位元組碼檔案對象。
  • 而解剖使用的就是Class類中的方法,是以先要擷取到每一個位元組碼檔案對應的Class類型的對象。
  • B:三種方式
  • a:Object類的getClass方法,判斷兩個對象是否是同一個位元組碼檔案
  • b:靜态屬性class,鎖對象
  • Class類中靜态方法forName(),讀取配置檔案
  • C:案例示範
  • 擷取class檔案對象的三種方式
public class Test {
	public static void main(String[] args) throws ClassNotFoundException{
   		Class clazz1 = Class.forName("Test.Person");
   		Class clazz2 = Person.class;
   		Person p = new Person();
   		Class clazz3 = p.getClass();

		System.out.println(clazz1 == clazz2); //true
		System.out.println(clazz1 == clazz3); //true
		System.out.println(clazz2 == clazz3); //true
	}
}
           

27.04_反射(class.forName()讀取配置檔案舉例)

  • 榨汁機(Juicer)榨汁的案例
  • 分别有水果(Fruit)蘋果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze)
package Test;

import java.io.BufferedReader;
import java.io.FileReader;

public class Test {
    public static void main(String[] args) throws Exception{
        //沒有用到反射,用到多态
        //Juicer j = new Juicer(); // 購買榨汁機
        //j.run(new Orange());
        //j.run(new Apple());

        //用反射和配置檔案
        BufferedReader br = new BufferedReader(new  FileReader("config.properties"));
        Class clazz = Class.forName(br.readLine());
        Fruit f = (Fruit)clazz.newInstance(); //相當于父類引用指向子類對象,水果引用指向蘋果對象
        Juicer j = new Juicer();
        j.run(f);
        //要建立不同的對象隻需要修改配置檔案config.properties中的類名即可
    }
}

interface Fruit {
    public void squeeze();
}

class Apple implements Fruit{
    public void squeeze() {
        System.out.println("榨出一杯蘋果汁");
    }
}

class Orange implements Fruit{
    public void squeeze() {
        System.out.println("榨出一杯桔子汁");
    }
}

class Juicer {
    public void run(Fruit f) {
        f.squeeze();
    }
}
           
  • 配置檔案config.properties
    • Test.Apple

27.05_反射(通過反射擷取帶參構造方法并使用)

  • Constructor
  • Class類的newInstance()方法是使用該類無參的構造函數建立對象,如果一個類沒有無參的構造函數,就不能這樣建立了,可以調用Class類的getConstructor(String.class,int.class)方法擷取一個指定的構造函數然後再調用Constructor類的newInstance(“張三”,20)方法建立對象
package Test;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("Test.Person");
        //Person p = (Person)clazz.newInstance(); //通過無參構造建立對象
        //Person類沒有空參構造時報錯,因為newInstance需要有無參的構造方法
        //System.out.println(p);
        Constructor c = clazz.getConstructor(String.class,int.class);
        //擷取有參構造
        Person p = (Person)c.newInstance("張三",23);
        //通過有參構造建立對象
        System.out.println(p);//name=張三,age=23
    }
}
           

27.06_反射(通過反射擷取成員變量并使用)

  • Field
  • Class.getField(String)方法可以擷取類中的指定字段(可見的),如果是私有的可以用getDeclaedField(“name”)方法擷取,通過set(obj,“李四”)方法可以設定指定對象上該字段的值,如果是私有的需要先調用setAccessible(true)設定通路權限,用擷取的指定的字段調用get(obj)可以擷取指定對象中該字段的值
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("Test.Person");
        //Person p = (Person)clazz.newInstance(); //通過無參構造建立對象
        //Person類沒有空參構造時報錯,因為newInstance需要有無參的構造方法
        //System.out.println(p);
        Constructor c = clazz.getConstructor(String.class,int.class);
        //擷取有參構造
        Person p = (Person)c.newInstance("張三",23);
        //通過有參構造建立對象
        //System.out.println(p);//name=張三,age=23

        //Field f = clazz.getDeclaredField("name");//拿到name字段
        //f.set(p,"李四"); //把對象p的name字段改成李四

        Field f = clazz.getDeclaredField("name");//暴力反射擷取字段
        f.setAccessible(true); //去除私有權限
        f.set(p,"李四");
        System.out.println(p);
    }
}
           

27.07_反射(通過反射擷取方法并使用)

  • Method
  • Class.getMethod(String,Class…)和Class.getDeclaredMethod(String,Class…)方法可以擷取類中的指定方法,調用invoke(Object,Object…)可以調用該方法,Class.getMethod(“eat”) invoke(obj) Class.getMethod(“eat”,int.class) invoke(obj,10)
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("Test.Person");
        Constructor c = clazz.getConstructor(String.class,int.class);//擷取有參構造
        Person p = (Person) c.newInstance("張三",23);//通過有參構造建立對象

        Method m = clazz.getMethod("eat");//擷取eat方法 無參
        m.invoke(p);

        Method m2 = clazz.getMethod("eat",int.class);//擷取eat方法 有參
        m2.invoke(p,10);
    }
}
           

27.08_反射(通過反射越過泛型檢查)

  • A:案例示範
  • ArrayList的一個對象,在這個集合中添加一個字元串資料,如何實作呢?
import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) throws Exception{
        //泛型隻在編譯期有效,在運作期會被擦除掉
        ArrayList<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);

        Class clazz = Class.forName("java.util.ArrayList"); //位元組碼檔案是在運作期
        //擷取位元組碼對象
        Method m = clazz.getMethod("add",Object.class); //擷取add方法
        m.invoke(list,"abc");
        System.out.println(list); //[111, 222, abc] 泛型反射
    }
}
           

27.09_反射(通過反射寫一個通用的設定某個對象的某個屬性為指定的值)

  • A:案例示範
  • public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj對象中名為propertyName的屬性的值設為value。
package Test;

import java.lang.reflect.Field;

public class Tool {
    public void setProperty(Object obj, String propertyName, Object value) throws Exception{
        Class clazz = obj.getClass();  //擷取位元組碼對象
        Field f = clazz.getDeclaredField(propertyName); //暴力反射擷取字段(屬性)
        f.setAccessible(true); //去除權限
        f.set(obj, value);
    }
}
           
package Test;

public class Test {
    public static void main(String[] args) throws Exception{
        Student s = new Student("張三",23);
        System.out.println(s);

        Tool t = new Tool();
        t.setProperty(s,"name","李四");
        System.out.println(s);
    }
}
           

27.10_反射(練習)

  • 已知一個類,定義如下
package Test;
public class DemoClass {
    public void run() {
		system.out.println("Welcome");
	}
}
           
  • (1) 寫一個Properties格式的配置檔案,配置類的完整名稱。
  • (2) 寫一個程式,讀取這個Properties配置檔案,獲得類的完整名稱并加載這個類,用反射的方式運作run方法。
  • xxx.properties
    • Test.Tool
package Test;

public class Tool {
    public void run() throws Exception{
        System.out.println("Welcome");
    }
}
           
package Test;

import java.io.BufferedReader;
import java.io.FileReader;

public class Test {
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new FileReader("xxx.properties"));
        //建立輸入流關聯配置檔案
        Class clazz = Class.forName(br.readLine()); //讀取配置檔案中類名,擷取位元組碼對象
        Tool t = (Tool)clazz.newInstance(); //通過位元組碼建立對象
        t.run();
    }
}
           

27.11_反射(動态代理的概述和實作)

  • A:動态代理概述
  • 代理:本來應該自己做的事情請了别人來做,被請的人就是代理對象
  • 舉例:春節回家買票讓人代買
  • 動态代理:在程式運作過程中産生的這個對象,而程式運作過程中産生對象其實就是剛才反射的内容,是以,動态代理其實就是通過反射來生成一個代理
  • 在Java中java.lang.reflect包下提供了提供了一個Proxy類和一個InvocationHandler接口,通過使用這個類和接口就可以生成動态代理對象。JDK提供的代理隻能針對接口做代理。我們有更強大的代理cglib,Proxy類中的方法建立動态代理類對象
  • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
  • 最終會調用InvocationHandler的方法
  • InvocationtionHander Object invoke(Object proxy, Method method, Object[] args)
package com.heima.動态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
	private Object target;
	
	public MyInvocationHandler(Object target) {
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("權限校驗");
		method.invoke(target, args);					//執行被代理target對象的方法
		System.out.println("日志記錄");
		return null;
	}
}
           

27.12_設計模式(模闆(Template)設計模式概述和使用)

  • A:模闆設計模式概述
  • 模闆方法模式就是定義一個算法的骨架,而将具體的算法延遲到子類中來實作
  • B:優點和缺點
  • 優點
    • 使用模闆方法模式,在定義算法骨架的同時,可以很靈活地實作具體的算法,滿足使用者靈活多變的需求
  • 缺點
    • 如果算法骨架有修改的話,則需要修改抽象類
  • 1.裝飾
  • 2.單例
  • 3.簡單工廠
  • 4.工廠方法
  • 5.擴充卡
  • 6.模闆
package com.heima.模版方法設計模式;

public class Demo1_Template {
	public static void main(String[] args) {
		/*long start = System.currentTimeMillis();
		for(int i = 0; i < 1000000; i++) {
			System.out.println("x");
		}
		long end = System.currentTimeMillis();
		System.out.println(end - start);*/
		Demo d = new Demo();
		System.out.println(d.getTime());
	}
}

abstract class GetTime {
	public final long getTime() {
		long start = System.currentTimeMillis();
		code();
		long end = System.currentTimeMillis();
		return end - start;
	}
	public abstract void code();
}

class Demo extends GetTime {
	@Override
	public void code() {
		int i = 0;
		while(i < 100000) {
			System.out.println("x");
			i++;
		}
	}
}
           

27.13_JDK5新特性(自己實作枚舉類)

  • A:枚舉概述
  • 是指将變量的值一一列出來,變量的值隻限于列舉出來的值的範圍内。舉例:一周隻有7天,一年隻有12個月等。
  • B:回想單例設計模式:單例類是一個類隻有一個執行個體
  • 那麼多例類就是一個類有多個執行個體,但不是無限個數的執行個體,而是有限個數的執行個體。這才能是枚舉類。
  • A:案例示範
  • 自己實作枚舉類
  • 空參構造
public class Week {
	
	public static final Week MON = new Week();
	public static final Week TUE = new Week();
	public static final Week WED = new Week();
	
	private Week(){}						//私有構造,不讓其他類建立本類對象
}
           
  • 有有參構造
public class Week2 {
	
	public static final Week2 MON = new Week2("星期一");
	public static final Week2 TUE = new Week2("星期二");
	public static final Week2 WED = new Week2("星期三");
	
	private String name;
	private Week2(String name){
		this.name = name;
	}						//私有構造,不讓其他類建立本類對象
	public String getName() {
		return name;
	}	
}
           
  • 第三種格式
public abstract class Week3 {	
	public static final Week3 MON = new Week3("星期一") {
		public void show() {
			System.out.println("星期一");
		}
	};
	public static final Week3 TUE = new Week3("星期二"){
		public void show() {
			System.out.println("星期二");
		}
	};
	public static final Week3 WED = new Week3("星期三"){
		public void show() {
			System.out.println("星期三");
		}
	};
	
	private String name;
	private Week3(String name){
		this.name = name;
	}						//私有構造,不讓其他類建立本類對象
	public String getName() {
		return name;
	}	
	public abstract void show();
}
           
public class Demo1_Enum {
	public static void main(String[] args) {
		//demo1();
		//demo2();
		Week3 mon = Week3.MON;
		mon.show();
	}

	public static void demo2() {
		Week2 mon = Week2.MON;
		System.out.println(mon.getName());
	}

	public static void demo1() {
		Week mon = Week.MON;
		Week tue = Week.TUE;
		Week wed = Week.WED;
		
		System.out.println(mon);
	}
}
           
  • 1.自動拆裝箱
  • 2.泛型
  • 3.可變參數
  • 4.靜态導入
  • 5.增強for循環
  • 6.互斥鎖
  • 7.枚舉

27.14_JDK5新特性(通過enum實作枚舉類)

  • A:案例示範
  • 通過enum實作枚舉類
public enum Week {
	MON,TUE,WED;
}
           
public enum Week2 {
	MON("星期一"),TUE("星期二"),WED("星期三");
	
	private String name;
	private Week2(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	
	public String toString() {
		return name;
	}
}
           
public enum Week3 {
	MON("星期一"){
		public void show() {
			System.out.println("星期一");
		}
	},TUE("星期二"){
		public void show() {
			System.out.println("星期二");
		}
	},WED("星期三"){
		public void show() {
			System.out.println("星期三");
		}
	};
	
	private String name;
	private Week3(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	
	public abstract void show();
	
}
           

27.15_JDK5新特性(枚舉的注意事項)

  • A:案例示範
  • 定義枚舉類要用關鍵字enum
  • 所有枚舉類都是Enum的子類
  • 枚舉類的第一行上必須是枚舉項,最後一個枚舉項的分号是可以省略的,但如果枚舉類有其他的東西,這個分号不能省略,建議不要省略
  • 枚舉類可以有構造器,但必須是private的,它預設的也是private的
  • 枚舉類也可以有抽象方法,但是枚舉項必須重寫該方法
  • 枚舉在switch語句中的使用

27.16_JDK5新特性(枚舉類的常見方法)

  • A:枚舉類的常見方法
  • int ordinal()
  • int compareTo(E o)
  • String name()
  • String toString()
  • T valueOf(Class type, String name)
  • values()
  • 此方法雖然在JDK文檔中查找不到,但每個枚舉類都有該方法,它周遊枚舉類的所有枚舉值非常友善
  • A:案例示範
  • 枚舉類的常見方法
public class Demo2_Enum {
	/**
	 * int ordinal()
	* int compareTo(E o)
	* String name()
	* String toString()
	* <T> T valueOf(Class<T> type,String name)
	* values() 
	* 此方法雖然在JDK文檔中查找不到,但每個枚舉類都具有該方法,它周遊枚舉類的所有枚舉值非常友善
	 */
	public static void main(String[] args) {
		//demo1();
//		Week2 mon = Week2.valueOf(Week2.class, "MON");	//通過位元組碼對象擷取枚舉項
//		System.out.println(mon);		
		Week2[] arr = Week2.values();
		for (Week2 week2 : arr) {
			System.out.println(week2);
		}
	}

	public static void demo1() {
		Week2 mon = Week2.MON;
		Week2 tue = Week2.TUE;
		Week2 wed = Week2.WED;
		
		/*System.out.println(mon.ordinal());				//枚舉項都是有編号的
		System.out.println(tue.ordinal());
		System.out.println(wed.ordinal());		
		System.out.println(mon.compareTo(tue));			//比較的是編号
		System.out.println(mon.compareTo(wed));*/		
		System.out.println(mon.name()); 				//擷取執行個體名稱
		System.out.println(mon.toString()); 			//調用重寫之後的toString方法
	}
}
           

27.17_JDK7新特性(JDK7的六個新特性回顧和講解)

  • A:二進制字面量0b001:System.out.println(0b110);
  • B:數字字面量可以出現下劃線:100_000
  • C:switch語句可以用字元串
  • D:泛型簡化,菱形泛型
  • E:異常的多個catch合并,每個異常用或|
  • F:try-with-resources語句,1.7版本标準的異常處理

27.18_JDK8新特性(JDK8的新特性)

  • 接口中可以定義有方法體的方法,如果是非靜态,必須用default修飾
  • 如果是靜态的就不用了