java-反射,介面新特性,Lambda表示式

簡單_319355229發表於2020-12-23

反射

概念

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。

在程式執行過程中動態的呼叫沒有書寫呼叫其他類的程式碼.

類載入器

將編譯好的class二進位制位元組碼檔案交由jvm進行執行

三種預設類載入器

**啟動類載入器:**將啟動jvm的class檔案進行載入執行

**擴充套件載入器:**將java執行基本jar包進行載入執行

**系統載入器:**將我們編寫的class檔案進行載入執行

類物件Class

java中萬物皆物件,在使用反射時,將所有自定義類都當做類物件,都是class 的物件

獲取類物件的三種方式

Class c1=Class.forName(“類名”);

使用Class類靜態方法內部通過類載入器載入指定類class物件

Class c2=類名.class;

通過指定類class屬性獲取指定類class物件

Class c3=obj.getClass();

通過指定類物件的getClass方法獲取指定class物件

		// Class可以代表所有類與Object不同的是object是所有類的父類
		// 所有類都是Class類的物件
		// 獲取一個類class物件的方式
		// 1、使用Class類靜態方法獲取
		Class c1 = Class.forName("com.yunhe.day1106.Student");

		// 2、使用類的class屬性獲取(這種方式獲取class物件必須宣告變數儲存)
		Class c2 = Student.class;

		// 3、使用指定類物件的getClass方法獲取
		Student s = new Student();
		Class c3 = s.getClass();
		
	
		Object newInstance = c1.newInstance();//呼叫指定class物件代表類的無參構造方法
		System.out.println(newInstance);

反射獲取構造方法

通過clas類提供的獲取構造方法的方法,返回構造方法物件代表指定類的構造方法

Constructor getConstructor(Class<?>… parameterTypes)
返回一個 Constructor 物件,它反映此 Class 物件所表示的類的指定公共構造方法。
Constructor<?>[] getConstructors()
返回一個包含某些 Constructor 物件的陣列,這些物件反映此 Class 物件所表示的類的所有公共構造方法。
Constructor getDeclaredConstructor(Class<?>… parameterTypes)
返回一個 Constructor 物件,該物件反映此 Class 物件所表示的類或介面的指定構造方法。
Constructor<?>[] getDeclaredConstructors()
返回 Constructor 物件的一個陣列,這些物件反映此 Class 物件表示的類宣告的所有構造方法。

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//通過反射獲取構造方法
public class ConstructorTest {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException,
			InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Class<?> c = Class.forName("com.yunhe.day1106.Student");

		// 通過反射獲取指定類的class物件
		// 通過class物件獲取指定類的構造方法物件
		// getConstructor(Class<?>... parameterTypes)
		// 通過引數獲取指定引數個數與型別的構造方法
		Constructor<?> c1 = c.getConstructor();// 無參公開的構造方法
		Object c1o = c1.newInstance();
		// System.out.println(c1o);
		// 執行構造方法 並傳入執行時需要的資料
		Constructor<?> c2 = c.getConstructor(String.class);// 獲取公開的引數型別為String的構造方法
		Object c2o = c2.newInstance("張三");
		// System.out.println(c2o);

		// Constructor<?>[] getConstructors()
		// 獲取當前class物件代表類的所有公開的構造方法
		Constructor<?>[] constructors = c.getConstructors();
		// for (Constructor<?> constructor : constructors) {
		// System.out.println(constructor);
		// }

		// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
		// 獲取指定引數的構造方法(包括私有構造方法)
		Constructor<?> c3 = c.getDeclaredConstructor(String.class, int.class);
		c3.setAccessible(true);// 為私有構造方法賦權
		Object c3o = c3.newInstance("張三", 18);
		System.out.println(c3o);

		// Constructor<?>[] getDeclaredConstructors()
		// 返回 Constructor 物件的一個陣列,這些物件反映此 Class 物件表示的類宣告的所有構造方法。

		Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
		for (Constructor<?> constructor : declaredConstructors) {
			System.out.println(constructor);
		}

	}

}

反射獲取屬性

Field getField(String name)
返回一個 Field 物件,它反映此 Class 物件所表示的類或介面的指定公共成員欄位。
Field[] getFields()
返回一個包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位。
Field getDeclaredField(String name)
返回一個 Field 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告欄位。
Field[] getDeclaredFields()
返回 Field 物件的一個陣列,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位。

import java.lang.reflect.Field;
import java.util.Arrays;

//通過反射獲取屬性
public class FieldTest {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
		Class<?> c = Student.class;
		Object o = c.newInstance();
		// Field getField(String name)
		// 獲取指定class物件所代表類的指定名字的公開屬性
		Field field = c.getField("name");
		//System.out.println(field);
		
		// Field[] getFields()
		// 返回一個包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位。
		Field[] fields = c.getFields();
		//System.out.println(Arrays.toString(fields));

		
		// Field getDeclaredField(String name)
		// 獲取指定class物件代表的類的指定名字的屬性物件(包含私有屬性)
		Field declaredField = c.getDeclaredField("age");
		//System.out.println(declaredField);
		
		// Field[] getDeclaredFields()
		// 返回 Field 物件的一個陣列,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位。
		Field[] declaredFields = c.getDeclaredFields();
		//System.out.println(Arrays.toString(declaredFields));
		System.out.println(o);
		
		//Field屬性物件的使用
		//私有屬性使用前進行賦權操作
		declaredField.setAccessible(true);
		//set方法為指定物件進行賦值
		declaredField.set(o, 18);
		field.set(o, "張三");

		//get方法獲取指定物件屬性的屬性
		Object object = field.get(o);
		System.out.println(object);	
	}
}

反射獲取方法

Method getMethod(String name, Class<?>… parameterTypes)
返回一個 Method 物件,它反映此 Class 物件所表示的類或介面的指定公共成員方法。
Method[] getMethods()
返回一個包含某些 Method 物件的陣列,這些物件反映此 Class 物件所表示的類或介面(包括那些由該類或介面宣告的以及從超類和超介面繼承的那些的類或介面)的公共 member 方法。
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
返回一個 Method 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告方法。
Method[] getDeclaredMethods()
返回 Method 物件的一個陣列,這些物件反映此 Class 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。

import java.lang.reflect.Method;

public class MethodTest {
	public static void main(String[] args) throws Exception {
		Class<?> c = Student.class;
		Object o = c.newInstance();
		// Method getMethod(String name, Class<?>... parameterTypes)
		// 返回一個 Method 物件,獲取指定名稱指定引數列表的方法物件(公開的)
		Method method = c.getMethod("setName", String.class);
		// System.out.println(method);

		// Method[] getMethods()
		// 返回一個包含某些 Method 物件的陣列,這些物件反映此 Class
		// 返回所有公開的方法(包括繼承父類的方法)
		Method[] methods = c.getMethods();
		// for (Method method2 : methods) {
		// System.out.println(method2);
		// }

		// Method getDeclaredMethod(String name, Class<?>... parameterTypes)
		// 獲取指定方法(包括私有)
		// 獲取的是在當前類中定義的方法
		Method declaredMethod = c.getDeclaredMethod("a",String.class);
		// System.out.println(declaredMethod);

		// Method[] getDeclaredMethods()
		// 返回 Method 物件的一個陣列,這些物件反映此 Class
		// 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。
		Method[] declaredMethods = c.getDeclaredMethods();
		// for (Method method2 : declaredMethods) {
		// System.out.println(method2);
		// }
		
		//方法的執行
		//私有方法執行前需要賦權
		method.invoke(o, "張三");
		
		declaredMethod.setAccessible(true);
		//使用方法物件invole方法呼叫傳入呼叫的物件與引數(無參則不寫)
		declaredMethod.invoke(o, "asd");
	}
}

總結

反射的功能:通過預先一定格式的程式碼書寫,可以在程式執行過程中動態的進行類的使用

反射的使用

1、獲取指定類的class物件

①使用類載入器進行載入指定類的全路徑

②使用類的class屬性進行獲取

③使用類物件的getclass方法進行獲取

2、構造方法

①構造方法的獲取:四種方法進行構造方法的獲取

②構造方法的使用:賦權、建立物件

3、屬性

①屬性的獲取:四種方法進行屬性的獲取(注意獲取屬性時是否可以獲取繼承的屬性)

②屬性的使用:賦權、賦值、獲取值(注意需要傳入賦值的物件與值)

4、方法

①方法的獲取:四種方式進行方法的獲取(注意獲取方法時是否可以獲取繼承的方法)

②方法的使用:賦權、執行(注意需要傳入執行方法的物件與引數)

介面中新特性

介面中的預設方法jdk1.8以後介面的新特性

定義:在介面中,使用關鍵字default修飾的方法

可以在介面中書寫擁有方法體的方法,使用 default進行修飾,實現介面的方法可以直接使用

public class InterFaceTest {
	public static void main(String[] args) {
		myTestInterface mif = new myTestInterface() {
			@Override
			public void a() {
				System.out.println("aaaaaaaa");
			}
		};
		mif.b();

	}
}

interface myTestInterface {
	public abstract void a();
	// 介面新特性
	// jdk1.8以後介面可以進行方法體方法的書寫
	// 稱之為介面的預設方法 使用default進行修飾
	default public void b() {
		System.out.println("bbbbbbb");
	}
}

如果一個實現類實現了多個介面,但是多個介面中存在方法名與引數都相同的預設方法,那麼實現類必須重寫這個方法,在方法中覺得呼叫哪個介面提供的預設方法

interface myTestInterface {
	public abstract void a();

	// 介面新特性
	// jdk1.8以後介面可以進行方法體方法的書寫
	// 稱之為介面的預設方法 使用default進行修飾
	default public void b() {
		System.out.println("1bbbbbbb");
	}
}

interface myTestInterface1 {
	// 介面新特性
	// jdk1.8以後介面可以進行方法體方法的書寫
	// 稱之為介面的預設方法 使用default進行修飾
	default public void b() {
		System.out.println("2bbbbbbb");
	}
}

class T implements myTestInterface,myTestInterface1{

	@Override
	public void a() {
	}
	@Override
	public void b() {
		//使用第一個介面的b方法進行實現
		//myTestInterface.super.b();
		//使用第二個介面的b方法進行實現
		//myTestInterface1.super.b();
		//自己實現
		System.out.println("3bbbbbb");
	}
}

Lambda表示式

Lambda 表示式是 jdk1.8 新增的重要特性,Lambda 使 Java 具有了類似函數語言程式設計的風格,其實 Lambda 表示式也是一個 “語法塊”,其實質也是由編譯器根據表示式推斷最終生成原始語法的位元組碼方式。
Lambda 表示式允許把函式作為一個方法的引數,或者把程式碼看成資料。用於簡化java中介面式的匿名內部類。(被稱為函式式介面:只用一個方法的普通介面)
lambda表示式用於簡化java中介面式的匿名內部類書寫,被稱為函式式介面(函式式介面:具有方法的介面)

lambda表示式,函數語言程式設計(由固定語法組成的公式進行程式碼編輯)的語法,主要用於實現函式式介面

函式式介面

指的是隻有一個抽象方法的介面,例如:runnable介面

使用註解@FunctionalInterface

用於進行函式式介面限制

@FunctionalInterface
public interface MyFunctionInterface {
	// 函式式介面
	// 只能書寫一個抽象方法
	// 使用註解@FunctionalInterface 進行限制
	public abstract void a();
}

lambda表示式的作用

lambda是一個可以傳遞的程式碼塊,可以在以後執行一次或多次。lambda表示式允許把函式作為一個方法的引數(函式作為引數傳遞進方法中)

基本語法

①無參方法的實現

lambda表示式可以看做是實現函式式介面的最佳實現方式

語法:()->{}

()內填寫引數

{}內書寫方法執行程式碼

@FunctionalInterface
public interface MyFunctionInterface {
	// 函式式介面
	// 只能書寫一個抽象方法
	// 使用註解@FunctionalInterface 進行限制
	public abstract void a();
}

使用匿名內部類實現函式式介面

public class T1 {
	public static void main(String[] args) {
		// 使用匿名內部類實現
		MyFunctionInterface mi1 = new MyFunctionInterface() {
			@Override
			public void a() {
				System.out.println("aaaaaa");
			}
		};
		mi1.a();
	}
}

使用lambda表示式實現函式式介面

public class T2 {
public static void main(String[] args) {

	// 使用lambda表示式實現
	MyFunctionInterface mi2 = () -> {
		System.out.println("aaaaaa");
	};
	mi2.a();
}

}

②有參方法的實現

lambda表示式對於方法的引數型別可以自動推導

@FunctionalInterface
public interface MyFunctionInterface {
	// 函式式介面
	// 只能書寫一個抽象方法
	// 使用註解@FunctionalInterface 進行限制
	public abstract void a(String name,int age);
}

使用匿名內部類實現函式式介面

public class T3 {
	public static void main(String[] args) {
		// 使用匿名內部類實現
		MyFunctionInterface mi1 = new MyFunctionInterface() {
			@Override
			public void a(String name, int age) {
				System.out.println("我叫" + name + ",今年" + age + "歲");
			}
		};
		mi1.a("張三", 18);
	}
}

使用lambda表示式實現函式式介面

public class T4 {
	public static void main(String[] args) {
		// 使用lambda表示式實現
		MyFunctionInterface mi2=(String name,int age)->{
			System.out.println("我叫" + name + ",今年" + age + "歲");
		};
		
		mi2.a("張三", 18);
		
		//lambda表示式可以根據方法進行資料型別的推導,所以在進書寫時可以省略資料型別的宣告
		MyFunctionInterface mi3=(name,age)->{
			System.out.println("我叫" + name + ",今年" + age + "歲");
		};
		mi3.a("張三", 18);
		
		//如果lambda表示式實現的方法引數只有唯一一個那麼()可以省略
	}
}

③有返回值方法的實現

使用匿名內部類實現函式式介面

public class T5 {
	public static void main(String[] args) {
		// 使用匿名內部類實現
		MyFunctionInterface mi1 = new MyFunctionInterface() {
			@Override
			public String a(String name) {
				return "我叫" + name;
			}
		};
		System.out.println(mi1.a("張三"));
	}
}

使用lambda表示式實現函式式介面

public class T6 {
	public static void main(String[] args) {
		// 使用lambda表示式實現
		// lambda表示式會自定識別方法的返回值型別無需書寫
		MyFunctionInterface mi2 = name -> {
			return "我叫" + name;
		};
		System.out.println(mi2.a("李四"));
	}
}

lambda表示式的優缺點

優點:1、簡潔 2、非常容易平行計算 3、可能代表未來的程式設計趨勢

缺點:1、如果不用平行計算,lambda表示式的計算速度一般不如for迴圈快(平行計算有時候也需要預熱才會顯示出效率優勢)

2、不容易除錯

3、如果其他人也沒學過lambda表示式,程式碼不容易讓其他語言的程式設計師看懂

4、lambda表示式只能用於實現函式式介面,如果介面中存在多個抽象方法將不能使用lambda表示式實現

使用lambda表示式進行map集合的遍歷

	public static void main(String[] args) {
		HashMap<Integer, Integer> map = new HashMap<>();
		for (int i = 0; i < 10; i++) {
			map.put(i, i);
		}

		// 使用迴圈遍歷
		// Set<Integer> keySet = map.keySet();
		// for (Integer integer : keySet) {
		// System.out.println(integer + "=>" + map.get(integer));
		// }

		// 這是一個用lambda表示式遍歷map的示例
		System.out.println("lambda");
		map.forEach((k, v) -> {
			System.out.println(k + "=>" + v);
		});
		//等價於
		map.forEach(new BiConsumer<Integer, Integer>() {
			@Override
			public void accept(Integer t, Integer u) {
				System.out.println(t + "=>" + u);
			}
		});
		
		//使用lambda表示式最常用例子
		//多執行緒的實現
		new Thread(()->{
			//多執行緒實現的方法
		}).start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				//多執行緒實現的方法
			}
		}).start();
		
	}

相關文章