今日內容
- JUnit單元測試-------------->必須重點掌握(簡單)
- 反射
- 獲取Class對象-------------->必須重點掌握(簡單)
- 反射操作構造方法
- 反射操作成員方法-------------->必須重點掌握(難點)
- 反射操作成員變量
- 注解
- 使用注解-------------->必須重點掌握(簡單)
- 自定義注解或者解析注解
- 動態代理
- 如何生成一個代理對象(動態代理)------>掌握(難點)
- JDK8新特性
- 方法引用------->建議掌握
- Base64編碼和解碼------->必須重點掌握(簡單)
第一章 Junit單元測試
1.1 Junit單元測試
Junit的概念
- 概述 : Junit是Java語言編寫的第三方單元測試框架(工具類)
- 作用 : 用來做“單元測試”——針對某個普通方法,可以像main()方法一樣獨立運行,它專門用于測試某個方法。
Junit的使用步驟
-
1.在模塊下創建lib文件夾,把Junit的jar包復制到lib文件夾中
-
2.選中Junit的jar包,右鍵選中 add as Library,把JUnit4的jar包添加到classPath中
-
3.在測試方法上面寫上@Test注解
-
4.執行測試方法
/** * Created by PengZhiLin on 2021/8/15 8:59 */ public class TestDemo { @Test public void test1(){ System.out.println("測試方法test1..."); } @Test public void test2(){ System.out.println("測試方法test2..."); } }
執行測試方法
-
1.選中方法名--->右鍵--->選中執行 只執行選中的測試方法
-
2.選中類名----->右鍵--->選中執行 執行該類中所有的測試方法
-
3.選中模塊---- ->右鍵--->選中all tests 執行 執行該模塊中所有的測試方法
-
如何查看測試結果
- 綠色:表示測試通過
- 紅色:表示測試失敗,有問題
Junit單元測試的注意事項
- 1.測試方法的權限修飾符一定是public
- 2.測試方法的返回值類型一定是void
- 3.測試方法一定沒有參數
- 4.測試方法 的聲明之上一定要使用@Test注解
1.2 Junit其他注解
- @Before:用來修飾方法,該方法會在每一個測試方法執行之前執行一次。
- @After:用來修飾方法,該方法會在每一個測試方法執行之后執行一次。
- @BeforeClass:用來修飾靜態方法,該方法會在所有測試方法之前執行一次,而且只執行一次。
- @AfterClass:用來修飾靜態方法,該方法會在所有測試方法之后執行一次,而且只執行一次。
/**
* Created by PengZhiLin on 2021/8/15 9:11
*/
public class TestDemo {
@BeforeClass
public static void bc(){
System.out.println("@BeforeClass修飾的方法...");
}
@Before
public void b(){
System.out.println("@Before修飾的方法...");
}
@Test
public void test1(){
System.out.println("測試方法test1...");
}
@Test
public void test2(){
System.out.println("測試方法test2...");
}
@After
public void a(){
System.out.println("@After修飾的方法...");
}
@AfterClass
public static void ac(){
System.out.println("@AfterClass修飾的方法...");
}
}
1.3 Junit斷言
-
斷言:預先判斷某個條件一定成立,如果條件不成立,則直接報錯。 使用Assert類中的assertEquals()方法
-
案例:
/** * Created by PengZhiLin on 2021/8/15 9:24 */ public class TestDemo { @Test public void test(){ // 斷言 /* Assert.assertEquals(參數1,參數2): 斷言 參數1: 期望的值 參數2: 實際的值 如果期望的值與實際的值相等,就不報錯,如果不相等,就報錯 */ int sum = getSum(10, 20); Assert.assertEquals(30,sum); } /** * 計算2個數的和 * @param num1 * @param num2 * @return 和 */ public int getSum(int num1,int num2){ return num1 * num2; } }
第二章 反射
2.1 類加載器
類的加載
- 當我們的程序在運行后,第一次使用某個類的時候,類加載器會將此類的class文件讀取到內存,并將此類的所有信息存儲到一個Class對象中

類的加載時機
-
創建類的實例。
-
類的靜態變量,或者為靜態變量賦值。
-
類的靜態方法。
-
使用反射方式來強制創建某個類或接口對應的java.lang.Class對象。
-
初始化某個類的子類對象。
-
直接使用java.exe命令來運行某個主類。
以上六種情況的任何一種,都可以導致JVM將一個類加載到方法區。
/** * Created by PengZhiLin on 2021/8/15 10:11 */ class Student extends Person { } public class Test { public static void main(String[] args) throws Exception { //1. 創建類的實例。 //new Person(); //2. 類的靜態變量,或者為靜態變量賦值。 //System.out.println(Person.num); //3. 類的靜態方法。 //Person.method(); //4. 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象。 //Class.forName("com.itheima.demo4_類加載時機.Person"); //5. 初始化某個類的子類對象。 new Student(); //6. 直接使用java.exe命令來運行某個主類。 // // 以上六種情況的任何一種,都可以導致JVM將一個類加載到方法區。 } }/** * Created by PengZhiLin on 2021/8/15 10:11 */ public class Person { static { System.out.println("Person 靜態代碼塊..."); } static int num = 10; public static void method(){ System.out.println("靜態method..."); } }
類加載器
? 類加載器:是負責將磁盤上的某個class文件讀取到內存并生成Class的對象。
/**
* Created by PengZhiLin on 2021/8/15 10:16
*/
public class Test {
public static void main(String[] args) {
// 需求:獲取Person類的類加載器
// 1.獲取Person類的字節碼對象
// 2.使用字節碼對象調用getClassLoader()方法
ClassLoader classLoader = Person.class.getClassLoader();
System.out.println("Person類的類加載器:"+classLoader);
// 需求:獲取String類的類加載器
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println("String類的類加載器:"+classLoader1);
// API中說明:一些實現可能使用null來表示引導類加載器。 如果此類由引導類加載器加載,則此方法將在此類實現中返回null
}
}
2.2 擴展類加載器結合Properties的使用
/**
* Created by PengZhiLin on 2021/8/15 10:27
*/
public class Utils {
public static String value1;
public static String value2;
public static String value3;
public static String value4;
static {
try {
// 1.創建Properties對象
Properties pro = new Properties();
// 2.調用load方法加載配置文件中的鍵值對
//pro.load(new FileInputStream("day13\\src\\db.properties"));
// 這種方式獲得的流,路徑已經到了src路徑
InputStream is = Utils.class.getClassLoader().getResourceAsStream("db.properties");
pro.load(is);
// 3.獲取數據
value1 = pro.getProperty("k1");
value2 = pro.getProperty("k2");
value3 = pro.getProperty("k3");
value4 = pro.getProperty("k4");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Created by PengZhiLin on 2021/8/15 10:23
*/
public class Test {
public static void main(String[] args) throws Exception{
System.out.println(Utils.value1);
System.out.println(Utils.value2);
System.out.println(Utils.value3);
System.out.println(Utils.value4);
}
}
2.3 反射的概述
反射的引入
- 問題:IDEA中的對象是怎么知道類有哪些屬性,哪些方法的呢?
通過反射技術對類進行了解剖得到了類的所有成員。
反射的概念
反射是一種機制,利用該機制可以在程序運行過程中對類進行解剖并操作類中的所有成員(成員變量,成員方法,構造方法)
使用反射操作類成員的前提
要獲得該類字節碼文件對象,就是Class對象
反射在實際開發中的應用
* 開發IDE(集成開發環境),比如IDEA,Eclipse
* 各種框架的設計和學習 比如Spring,Hibernate,Struct,Mybaits....
2.4 獲取Class對象
-
通過類名.class獲得
-
通過對象名.getClass()方法獲得
-
通過Class類的靜態方法獲得: static Class forName("類全名")
-
示例代碼
/** * Created by PengZhiLin on 2021/8/15 10:51 */ public class Test { public static void main(String[] args) throws Exception{ //- 通過類名.class獲得 Class<Person> c1 = Person.class; //- 通過對象名.getClass()方法獲得 Person p = new Person(); Class<? extends Person> c2 = p.getClass(); //- 通過Class類的靜態方法獲得: static Class forName("類全名") Class<?> c3 = Class.forName("com.itheima.demo7_獲取Class對象.Person"); System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c1 == c2);// true System.out.println(c2 == c3);// true } }
2.5 Class類常用方法
String getSimpleName(); 獲得類名字符串:類名
String getName(); 獲得類全名:包名+類名
T newInstance() ; 創建Class對象關聯類的對象---相當于調用該類的空參構造方法
-
示例代碼
/** * Created by PengZhiLin on 2021/8/15 10:54 */ public class Test { public static void main(String[] args)throws Exception { // 獲取Person類的字節碼對象 Class<Person> c = Person.class; //String getSimpleName(); 獲得類名字符串:類名 System.out.println("類的名稱:" + c.getSimpleName()); //String getName(); 獲得類全名:包名+類名 System.out.println("類的全名: " + c.getName()); //T newInstance() ; 創建Class對象關聯類的對象---相當于調用該類的空參構造方法 Person p = c.newInstance(); // Person p = new Person(); System.out.println(p); } }
2.6 反射之操作構造方法
Constructor類概述
Constructor類概述
* 類中的每一個構造方法都是一個Constructor類的對象
通過反射獲取類的構造方法
Class類的方法
1. Constructor getConstructor(Class... parameterTypes)
* 根據參數類型獲得對應的Constructor對象。
* 只能獲得public修飾的構造方法
2. Constructor getDeclaredConstructor(Class... parameterTypes)---推薦
* 根據參數類型獲得對應的Constructor對象
* 可以是public、protected、(默認)、private修飾符的構造方法。
3. Constructor[] getConstructors()
獲得類中的所有構造方法對象,只能獲得public的
4. Constructor[] getDeclaredConstructors()---推薦
獲得類中的所有構造方法對象
可以是public、protected、(默認)、private修飾符的構造方法。
/**
* Created by PengZhiLin on 2021/8/15 10:58
*/
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
}
private Person(int age) {
this.age = age;
}
}
/**
* Created by PengZhiLin on 2021/8/15 10:58
*/
public class Test {
public static void main(String[] args) throws Exception {
// 獲取Person類的字節碼對象
Class<Person> c = Person.class;
// 需求:獲取Person類的構造方法
// 獲取第1個構造方法
Constructor<Person> con1 = c.getDeclaredConstructor();
System.out.println("con1:" + con1);
// 獲取第2個構造方法
Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println("con2:" + con2);
// 獲取第3個構造方法
Constructor<Person> con3 = c.getDeclaredConstructor(String.class);
System.out.println("con3:" + con3);
// 獲取第4個構造方法
Constructor<Person> con4 = c.getDeclaredConstructor(int.class);
System.out.println("con4:" + con4);
// 需求:獲取所有的構造方法
Constructor<?>[] arr = c.getDeclaredConstructors();
for (Constructor<?> con : arr) {
System.out.println(con);
}
}
}
通過反射執行構造方法
Constructor對象常用方法
1. T newInstance(Object... initargs)
根據指定的參數創建對象
參數:被執行的構造方法需要的實際參數
2. void setAccessible(true)
設置"暴力反射"——是否取消權限檢查,true取消權限檢查,false表示不取消
示例代碼
/**
* Created by PengZhiLin on 2021/8/15 10:58
*/
public class Test {
public static void main(String[] args) throws Exception {
// 獲取Person類的字節碼對象
Class<Person> c = Person.class;
// 需求:獲取Person類的構造方法
// 獲取第1個構造方法
Constructor<Person> con1 = c.getDeclaredConstructor();
System.out.println("con1:" + con1);
// 獲取第2個構造方法
Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println("con2:" + con2);
// 獲取第3個構造方法
Constructor<Person> con3 = c.getDeclaredConstructor(String.class);
System.out.println("con3:" + con3);
// 獲取第4個構造方法
Constructor<Person> con4 = c.getDeclaredConstructor(int.class);
System.out.println("con4:" + con4);
// 需求:獲取所有的構造方法
Constructor<?>[] arr = c.getDeclaredConstructors();
for (Constructor<?> con : arr) {
System.out.println(con);
}
// 通過反射執行構造方法
// 執行第1個構造方法
Person p1 = con1.newInstance();
System.out.println("p1:" + p1);
// 執行第2個構造方法
Person p2 = con2.newInstance("黑馬程序員", 18);
System.out.println("p2:" + p2);
// 執行第3個構造方法
Person p3 = con3.newInstance("傳智播客");
System.out.println("p3:" + p3);
// 執行第4個構造方法
con4.setAccessible(true);// 取消權限檢查
Person p4 = con4.newInstance(19);
System.out.println("p4:" + p4);
}
}
2.7 反射之操作成員方法
Method類概述
Method類概述
* 每一個成員方法都是一個Method類的對象。
通過反射獲取類的成員方法
Class類中與Method相關的方法
* Method getMethod(String name,Class... args);
* 根據方法名和參數類型獲得對應的成員方法對象,只能獲得public的
* Method getDeclaredMethod(String name,Class... args);----->推薦
* 根據方法名和參數類型獲得對應的成員方法對象,包括public、protected、(默認)、private的
參數1:要獲取的方法的方法名
參數2:要獲取的方法的形參類型的Class對象
* Method[] getMethods();
* 獲得類中的所有成員方法對象,返回數組,只能獲得public修飾的且包含父類的
* Method[] getDeclaredMethods();----->推薦
* 獲得類中的所有成員方法對象,返回數組,只獲得本類的,包括public、protected、(默認)、private的
/**
* Created by PengZhiLin on 2021/8/15 11:17
*/
public class Person {
public void method1() {
System.out.println("無參數無返回值的成員方法...");
}
public void method2(int num) {
System.out.println("有參數無返回值的成員方法...,參數num的值:" + num);
}
public int method3() {
System.out.println("無參數有返回值的成員方法...");
return 100;
}
private int method4(String name, int age) {
System.out.println("有參數有返回值的成員方法...參數name的值:" + name + "參數age的值:" + age);
return 200;
}
}
/**
* Created by PengZhiLin on 2021/8/15 11:17
*/
public class Test {
public static void main(String[] args) throws Exception{
// 獲取Person類的字節碼對象
Class<Person> c = Person.class;
// 需求: 獲取成員方法對象
// 獲得第1個成員方法
Method m1 = c.getDeclaredMethod("method1");
// 獲得第2個成員方法
Method m2 = c.getDeclaredMethod("method2", int.class);
// 獲得第3個成員方法
Method m3 = c.getDeclaredMethod("method3");
// 獲得第4個成員方法
Method m4 = c.getDeclaredMethod("method4", String.class, int.class);
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);
System.out.println(m4);
// 需求:獲取所有成員方法對象
Method[] arr = c.getDeclaredMethods();
for (Method method : arr) {
System.out.println(method);
}
}
}
通過反射執行成員方法
Method對象常用方法
* Object invoke(Object obj, Object... args)
* 參數1:調用該方法的對象
* 參數2:調用該法時傳遞的實際參數
返回值:該方法執行完畢后的返回值
* void setAccessible(true)
設置"暴力訪問"——是否取消權限檢查,true取消權限檢查,false表示不取消
示例代碼
/**
* Created by PengZhiLin on 2021/8/15 11:17
*/
public class Test {
public static void main(String[] args) throws Exception {
// 獲取Person類的字節碼對象
Class<Person> c = Person.class;
// 需求: 獲取成員方法對象
// 獲得第1個成員方法
Method m1 = c.getDeclaredMethod("method1");
// 獲得第2個成員方法
Method m2 = c.getDeclaredMethod("method2", int.class);
// 獲得第3個成員方法
Method m3 = c.getDeclaredMethod("method3");
// 獲得第4個成員方法
Method m4 = c.getDeclaredMethod("method4", String.class, int.class);
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);
System.out.println(m4);
// 需求:獲取所有成員方法對象
Method[] arr = c.getDeclaredMethods();
for (Method method : arr) {
System.out.println(method);
}
System.out.println("--------------");
// 通過反射執行成員方法
// 通過反射創建Person對象
Person p = c.newInstance();
//Person p = c.getDeclaredConstructor().newInstance();
// 通過反射執行第1個成員方法
m1.invoke(p);// 等價于:p.method1();
// 通過反射執行第2個成員方法
m2.invoke(p, 10);// 等價于:p.method2(10);
// 通過反射執行第3個成員方法
Object res1 = m3.invoke(p);// 等價于: Object res1 = p.method3();
System.out.println("res1:" + res1);
// 通過反射執行第4個成員方法
m4.setAccessible(true);// 取消權限檢查
Object res4 = m4.invoke(p, "張三", 19);
System.out.println("res4:"+res4);// 等價于: Object res2 = p.method4("張三", 19);
}
}
2.8 反射之操作成員變量【自學】
Field類概述
Field類概述
* 每一個成員變量都是一個Field類的對象。
通過反射獲取類的成員變量
Class類中的方法
* Field getField(String name);
* 根據成員變量名獲得對應Field對象,只能獲得public修飾
參數:屬性名
* Field getDeclaredField(String name);----->推薦
* 根據成員變量名獲得對應Field對象,包括public、protected、(默認)、private的
參數:屬性名
* Field[] getFields();
* 獲得所有的成員變量對應的Field對象,只能獲得public的
* Field[] getDeclaredFields();---->推薦
* 獲得所有的成員變量對應的Field對象,包括public、protected、(默認)、private的
/**
* Created by PengZhiLin on 2021/8/15 11:50
*/
public class Person {
public String name;
private int age;
}
/**
* Created by PengZhiLin on 2021/8/15 11:52
*/
public class Test {
public static void main(String[] args)throws Exception {
// 獲得Person類的字節碼對象
Class<Person> c = Person.class;
// 通過反射獲取類的成員變量
// 獲得第1個成員變量
Field f1 = c.getDeclaredField("name");
// 獲得第2個成員變量
Field f2 = c.getDeclaredField("age");
System.out.println(f1);
System.out.println(f2);
// 獲取所有的成員變量
Field[] arr = c.getDeclaredFields();
for (Field field : arr) {
System.out.println(field);
}
}
}
通過反射訪問成員變量
Field對象常用方法
給對象的屬性賦值的方法
void set(Object obj, Object value) ----->推薦
參數1: 給哪個對象的屬性賦值---該類的對象
參數2: 給屬性賦的值
void setInt(Object obj, int i)
void setLong(Object obj, long l)
void setBoolean(Object obj, boolean z)
void setDouble(Object obj, double d)
...
獲取對象屬性的值的方法
Object get(Object obj) ----->推薦
int getInt(Object obj)
long getLong(Object obj)
boolean getBoolean(Object ob)
double getDouble(Object obj)
...
void setAccessible(true);暴力反射,設置為可以直接訪問私有類型的屬性。 ----->推薦
Class getType(); 獲取屬性的類型,返回Class對象。
setXxx方法都是給對象obj的屬性設置使用,針對不同的類型選取不同的方法。
getXxx方法是獲取對象obj對應的屬性值的,針對不同的類型選取不同的方法。
示例代碼
/**
* Created by PengZhiLin on 2021/8/15 11:52
*/
public class Test {
public static void main(String[] args) throws Exception {
// 獲得Person類的字節碼對象
Class<Person> c = Person.class;
// 通過反射獲取類的成員變量
// 獲得第1個成員變量
Field f1 = c.getDeclaredField("name");
// 獲得第2個成員變量
Field f2 = c.getDeclaredField("age");
System.out.println(f1);
System.out.println(f2);
// 獲取所有的成員變量
Field[] arr = c.getDeclaredFields();
for (Field field : arr) {
System.out.println(field);
}
// 通過反射得到Person類的對象
Person p = c.newInstance();
// 通過反射訪問成員變量
// 訪問第1個成員變量
f1.set(p, "張三");
System.out.println("p對象的name屬性值:" + f1.get(p));
// 訪問第2個成員變量
f2.setAccessible(true);// 取消權限檢查
f2.set(p,18);
System.out.println("p對象的age屬性值:" + f2.get(p));
// 獲取成員變量的類型
System.out.println(f1.getType());
System.out.println(f2.getType());
}
}
第三章 注解
3.1 注解概述
注解概述
-
注解(annotation),是一種代碼級別的說明,和類 接口平級關系.
-
注解(Annotation)相當于一種標記,在程序中加入注解就等于為程序打上某種標記,以后,javac編譯器、開發工具和其他程序可以通過反射來了解你的類及各種元素上有無標記,看你的程序有什么標記,就去干相應的事
-
我們之前使用過的注解:
? 1).@Override:子類重寫方法時——編譯時起作用
? 2).@FunctionalInterface:函數式接口——編譯時起作用
? 3).@Test:JUnit的測試注解——運行時起作用
-
注解的作用
-
生成幫助文檔:@author和@version
-
執行編譯期的檢查 例如:@Override
-
框架的配置(框架=代碼+配置)
- 具體使用請關注框架課程的內容的學習。
3.2 JDK提供的三個基本的注解
? @Override:描述方法的重寫.
? @SuppressWarnings:壓制\忽略警告.
? @Deprecated:標記過時
/**
* Created by PengZhiLin on 2021/8/15 12:07
*/
public class Test {
public static void main(String[] args) {
@SuppressWarnings("all")
int num = 10;
method1();
}
@Override
public String toString() {
return super.toString();
}
@Deprecated
public static void method1(){
}
}
3.3 自定義注解
-
格式
public @interface 注解名{ 注解屬性
}
- 注解屬性
- 格式: `數據類型 屬性名();`
- 屬性類型
? 1.基本類型
? 2.String
? 3.Class類型
? 4.注解類型
? 5. 枚舉類型
? 6.以上類型的一維數組類型
- 示例代碼
```java
// 有屬性的注解
public @interface MyAnnotation2 {
//? 1.基本類型(4類8種)
int a();
byte b();
//? 2.String
String str();
//? 3.Class類型
Class c();
//? 4.注解類型
MyAnnotation1 ma();
//? 5. 枚舉類型
Sex sex();
//? 6.以上類型的一維數組類型
int[] arr1();
String[] arr2();
Class[] arr3();
MyAnnotation1[] arr4();
Sex[] arr5();
}
/**
* Created by PengZhiLin on 2021/8/15 12:22
*/
// 無屬性的注解
public @interface MyAnnotation1 {
}
3.4 使用注解
- 使用注解:
如果一個注解沒有屬性,那么就不需要給注解屬性賦值,直接使用即可 @注解名
如果一個注解中有屬性,那么使用注解的時候一定要給注解所有屬性賦值 @注解名(屬性名=值,屬性名2=值2,...)
如何給注解屬性賦值:
@注解名(屬性名=值,屬性名2=值2,...)
-
案例演示
// 沒有屬性的注解 public @interface MyAnnotation1 { } // 有屬性的注解 public @interface MyAnnotation2 { String name(); int age(); }/** * Created by PengZhiLin on 2021/8/15 14:31 */ @MyAnnotation1 @MyAnnotation2(name="itheima",age=100) public class Test { @MyAnnotation1 @MyAnnotation2(name="itheima",age=100) String str; @MyAnnotation1 @MyAnnotation2(name="itheima",age=100) public void method(){ @MyAnnotation1 @MyAnnotation2(name="itheima",age=100) int num = 10; } }
3.5 使用注解的注意事項
-
一旦注解有屬性了,使用注解的時候,屬性必須有值
-
若屬性類型是一維數組的時候,當數組的值只有一個的時候可以省略{}
-
如果注解中只有一個屬性,并且屬性名為value,那么使用注解給注解屬性賦值的時候,注解屬性名value可以省略
-
注解屬性可以有默認值 格式:屬性類型 屬性名() defaul t 默認值;
// 有屬性的注解 public @interface MyAnnotation1 { String name(); int age(); } // 有屬性的注解 public @interface MyAnnotation2 { String[] names(); } // 有屬性的注解 public @interface MyAnnotation3 { String value(); } // 有屬性的注解 public @interface MyAnnotation33 { String[] value(); } // 有屬性的注解 public @interface MyAnnotation4 { String name() default "itheima"; int age() default 100; }/** * Created by PengZhiLin on 2021/8/15 14:36 */ public class Test { //- 一旦注解有屬性了,使用注解的時候,屬性必須有值 @MyAnnotation1(name="itheima",age=100) public void method1(){ } //- 若屬性類型是一維數組的時候,當數組的值只有一個的時候可以省略{} //@MyAnnotation2(names={"itheima"}) // 標準寫法 @MyAnnotation2(names="itheima") // 省略寫法 public void method2(){ } //- 如果注解中只有一個屬性,并且屬性名為value,那么使用注解給注解屬性賦值的時候,注解屬性名value可以省略 //@MyAnnotation3(value="java") // 標準寫法 @MyAnnotation3("java") // 省略寫法 public void method3(){ } //@MyAnnotation33(value={"java"}) // 標準寫法 @MyAnnotation33("java") // 標準寫法 public void method33(){ } //- 注解屬性可以有默認值 格式:屬性類型 屬性名() default 默認值; //@MyAnnotation4 @MyAnnotation4(name="itcast") public void method4(){ } }
3.6 元注解
什么是元注解
? 定義在注解上的注解(修飾注解的注解)
常見的元注解
? @Target:表示該注解作用在什么上面(位置),默認注解可以在任何位置. 值為:ElementType的枚舉值
? METHOD:方法
? TYPE:類 接口
? FIELD:字段
? CONSTRUCTOR:構造方法聲明
? LOCAL_VARIABLE:局部變量
? ....
/**
* Created by PengZhiLin on 2021/8/15 14:49
*/
@Target(value={ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation1 {
}
? @Retention:定義該注解保留到那個代碼階段, 值為:RetentionPolicy類型,默認只在源碼階段保留
? SOURCE:只在源碼上保留(默認)
? CLASS:在源碼和字節碼上保留
? RUNTIME:在所有的階段都保留
.java (源碼階段) ----編譯---> .class(字節碼階段) ----加載內存--> 運行(RUNTIME)
案例:
//@Retention(value= RetentionPolicy.SOURCE) // 該注解只能保留到源碼階段
//@Retention(value= RetentionPolicy.CLASS) // 該注解只能保留到字節碼階段
@Retention(value= RetentionPolicy.RUNTIME) // 該注解只能保留到運行階段
public @interface MyAnnotation2 {
}
3.7 注解解析
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等實現了AnnotatedElement
- T getAnnotation(Class
annotationType):得到指定類型的注解對象。沒有返回null。 - boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判斷指定的注解有沒有。
// 有屬性的注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name();
int age();
}
/**
* Created by PengZhiLin on 2021/8/15 14:59
*/
public class Test {
public static void main(String[] args) throws Exception {
// 需求:獲取method1方法上注解的屬性值
// 1.獲取method1方法上的注解
// 1.1 獲取Test類的字節碼對象
Class<Test> c = Test.class;
// 1.2 獲取Test類中method1方法對應的Method對象
Method m1 = c.getDeclaredMethod("method1");
// 1.3 根據Method對象調用getAnnotation方法獲得注解對象
MyAnnotation annotation = m1.getAnnotation(MyAnnotation.class);
// 2.根據method1方法上的注解獲取屬性值
System.out.println(annotation.name());
System.out.println(annotation.age());
// 需求: 執行Test類中含有MyAnnotation注解的所有方法
// 1.獲取Test類中所有的方法
Method[] arr = c.getDeclaredMethods();
// 2.通過反射創建Test對象
Test test = c.newInstance();
// 3.循環遍歷所有的方法
for (Method m : arr) {
// 3.判斷遍歷出來的方法上是否有MyAnnotation注解
boolean flag = m.isAnnotationPresent(MyAnnotation.class);
// 4.如果有,就執行
if (flag){
m.invoke(test);
}
}
}
@MyAnnotation(name = "itheima", age = 100)
public void method1() {
System.out.println("method1方法....");
}
@MyAnnotation(name = "itheima", age = 100)
public void method2() {
System.out.println("method2方法....");
}
public void method3() {
System.out.println("method3方法....");
}
}
3.8 完成注解的MyTest案例
需求
? 在一個類(測試類,TestDemo)中有三個方法,其中兩個方法上有@MyTest,另一個沒有.還有一個主測試類(MainTest)中有一個main方法. 在main方法中,讓TestDemo類中含有@MyTest方法執行. 自定義@MyTest, 模擬單元測試.
思路分析
-
定義兩個類和一個注解
-
在MainTest的main()方法里面:
? //1.獲得TestDemo字節碼對象
? //2.反射獲得TestDemo里面的所有的方法
? //3.遍歷方法對象的數組. 判斷是否有@MyTest(isAnnotationPresent)
? //4.有就執行(method.invoke())
代碼實現
- MyTest.java
/**
* Created by PengZhiLin on 2021/8/15 15:16
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
- TestDemo.java
/**
* Created by PengZhiLin on 2021/8/15 15:16
*/
public class TestDemo {
@MyTest
public void test1(){
System.out.println("測試方法test1...");
}
public void test2(){
System.out.println("測試方法test2...");
}
@MyTest
public void test3(){
System.out.println("測試方法test3...");
}
}
- MainTest.java
/**
* Created by PengZhiLin on 2021/8/15 15:17
*/
public class MainTest {
public static void main(String[] args) throws Exception{
// 1.獲得TestDemo類的字節碼對象
Class<TestDemo> c = TestDemo.class;
// 2.獲得TestDemo類中的所有方法
Method[] arr = c.getDeclaredMethods();
// 通過反射創建TestDemo對象
TestDemo td = c.newInstance();
// 3.循環遍歷所有的方法
for (Method m : arr) {
// 4.判斷方法上是否有@MyTest注解
boolean flag = m.isAnnotationPresent(MyTest.class);
// 5.如果就,就執行該方法
if (flag){
m.invoke(td);
}
}
}
}
第四章 動態代理
代理模式概述
為什么要有“代理”?生活中就有很多代理的例子,例如,我現在需要出國,但是我不愿意自己去辦簽證、預定機票和酒店(覺得麻煩 ,那么就可以找旅行社去幫我辦,這時候旅行社就是代理,而我自己就是被代理了。
代理模式的定義:被代理者沒有能力或者不愿意去完成某件事情,那么就需要找個人代替自己去完成這件事,這個人就是代理者, 所以代理模式包含了3個角色: 被代理角色 代理角色 抽象角色(協議)
靜態代理:
/**
* Created by PengZhiLin on 2021/8/15 15:46
*/
public interface FindHappy {
void happy();
}
/**
* Created by PengZhiLin on 2021/8/15 15:46
*/
public class JinLian implements FindHappy {
@Override
public void happy() {
System.out.println("金蓮正在happy...");
}
}
/**
* Created by PengZhiLin on 2021/8/15 15:46
*/
public class YanPoXi implements FindHappy {
@Override
public void happy() {
System.out.println("閻婆惜正在happy...");
}
}
/**
* Created by PengZhiLin on 2021/8/15 15:48
*/
public class WangPo implements FindHappy {
FindHappy findHappy;
public WangPo(FindHappy findHappy) {
this.findHappy = findHappy;
}
@Override
public void happy() {
System.out.println("王婆以做衣服的名義開好房間...");
findHappy.happy();
System.out.println("王婆打掃戰場...");
}
}
/**
* Created by PengZhiLin on 2021/8/15 15:46
*/
public class Test {
public static void main(String[] args) {
// 創建金蓮對象---被代理者
JinLian jinLian = new JinLian();
//jinLian.happy();
// 創建王婆對象---代理者
WangPo wp = new WangPo(jinLian);
wp.happy();
System.out.println("---------------");
// 創建閻婆惜對象---被代理者
YanPoXi ypx = new YanPoXi();
// 創建王婆對象---代理者
WangPo wp1 = new WangPo(ypx);
wp1.happy();
}
}
動態代理介紹
-
概述 : 動態代理就是在程序運行期間,直接通過反射生成一個代理對象,代理對象所屬的類是不需要存在的
-
動態代理的獲取:
? jdk提供一個Proxy類可以直接給實現接口類的對象直接生成代理對象
動態代理相關api介紹
Java.lang.reflect.Proxy類可以直接生成一個代理對象
- Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一個代理對象
- 參數1:ClassLoader loader 被代理對象的類加載器
- 參數2:Class<?>[] interfaces 被代理實現的所有接口 的Class對象
- 參數3:InvocationHandler h (接口)執行處理類
- 返回值: 代理對象
- 前2個參數是為了幫助在jvm內部生成被代理對象的代理對象,第3個參數,用來監聽代理對象調用方法,幫助我們調用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:調用代理類的任何方法,此方法都會執行
- 參數1:代理對象(慎用)
- 參數2:當前執行的方法
- 參數3:當前執行的方法運行時傳遞過來的參數
- 返回值:當前方法執行的返回值
案例演示
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by PengZhiLin on 2021/8/15 15:59
*/
public class Test {
public static void main(String[] args) {
// 1.創建金蓮對象
JinLian jinLian = new JinLian();
// 程序運行到這里,直接通到反射生成一個代理對象
// 2.獲得被代理類的類加載器
ClassLoader classLoader = jinLian.getClass().getClassLoader();
// 3.獲得被代理類實現的所有接口的Class對象
Class<?>[] interfaces = jinLian.getClass().getInterfaces();
// 4.處理接口---用來監聽代理對象的
InvocationHandler iv = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 只要代理對象調用方法,就會來到這里執行
// 參數1: 代理對象(慎用)
// 參數2: 代理對象調用的方法
// 參數3: 代理對象調用方法時傳遞的實際參數
// 返回值: 代理對象調用的方法的返回值
if (method.getName().equals("happy")){
System.out.println("王婆以做衣服的名義開好房間...");
//jinLian.happy();
method.invoke(jinLian,args);
System.out.println("王婆打掃戰場...");
}
if (method.getName().equals("toString")){
System.out.println("toString...");
}
return null;
}
};
// 5.動態生成一個代理對象
FindHappy proxy = (FindHappy)Proxy.newProxyInstance(classLoader,interfaces,iv);
// 6.使用代理對象
proxy.happy();
System.out.println("-----------");
proxy.toString();
}
}
第五章JDK8新特性
5.1 方法引用
方法引用概述
- 方法引用使用一對冒號 :: , 方法引用就是用來在一定的情況下,替換Lambda表達式
方法引用基本使用
- 使用場景:
- 如果一個Lambda表達式大括號中的代碼和另一個方法中的代碼一模一樣,那么就可以使用方法引用把該方法引過來,從而替換Lambda表達式
- 如果一個Lambda表達式大括號中的代碼就是調用另一方法,那么就可以使用方法引用把該方法引過來,從而替換Lambda表達式
/**
* Created by PengZhiLin on 2021/8/15 16:37
*/
public class Test {
public static void printStr(){
System.out.println("Hello World...");
System.out.println("Hello World...");
System.out.println("Hello World...");
}
public static void main(String[] args) {
// 創建并啟動線程
/* new Thread(()->{
System.out.println("Hello World...");
System.out.println("Hello World...");
System.out.println("Hello World...");
}).start();*/
/*new Thread(()->{
Test.printStr();
}).start();*/
new Thread(Test::printStr).start();
}
}
5.2 方法引用的分類
構造方法引用
/**
* Created by PengZhiLin on 2021/8/15 16:43
*/
public class Test1_引用構造方法 {
public static void main(String[] args) {
/*
引入構造方法的格式: 類名::new
*/
//創建集合
ArrayList<String> list = new ArrayList<>();
list.add("楊紫");
list.add("迪麗熱巴");
list.add("陳鈺琪");
// 需求: 使用Stream流把集合中的元素轉換為Person對象,打印輸出
//list.stream().map((String name)->{return new Person(name);}).forEach(p->System.out.println(p));
// map方法中的Lambda表達式大括號中就是調用Person類的構造方法,可以使用方法引用
list.stream().map(Person::new).forEach(p->System.out.println(p));
}
}
靜態方法引用
/**
* Created by PengZhiLin on 2021/8/15 16:50
*/
public class Test2_引用靜態方法 {
public static void main(String[] args) {
/*
引用靜態方法的格式: 類名::方法名
*/
//創建集合
ArrayList<String> list = new ArrayList<>();
list.add("110");
list.add("111");
list.add("112");
// 需求: 使用Stream流把集合中的元素轉換為int值,打印輸出
//list.stream().map((String str)->{return Integer.parseInt(str);}).forEach(i-> System.out.println(i+1));
// map方法的Lambda表達式大括號中就是調用Integer的parseInt方法,可以使用方法引用
list.stream().map(Integer::parseInt).forEach(i-> System.out.println(i+1));
}
}
有參數成員方法引用
-
成員方法有參數
/** * Created by PengZhiLin on 2021/8/15 16:53 */ public class Test3_引用有參數的成員方法 { public static void main(String[] args) { /* 成員方法有參數引用: 對象名::方法名 */ //創建集合 ArrayList<String> list = new ArrayList<>(); list.add("楊紫"); list.add("迪麗熱巴"); list.add("陳鈺琪"); // 需求:使用Stream流把集合中所有元素打印輸出 //list.stream().forEach(name-> System.out.println(name)); // froEach方法的Lambda表達式就是調用System.out的println()方法,可以使用方法引用 list.stream().forEach(System.out::println); } }
沒有參數成員方法引用
-
成員方法沒有參數
/** * Created by PengZhiLin on 2021/8/15 16:56 */ public class Test4_引用沒有參數的成員方法 { public static void main(String[] args) { /* 成員方法沒有參數引用: 類名::方法名 */ //創建集合 ArrayList<String> list = new ArrayList<>(); list.add("楊紫"); list.add("迪麗熱巴"); list.add("陳鈺琪"); // 需求: 使用Stream流把集合中的元素轉換為該元素對應的字符長度,打印輸出 //list.stream().map((String name)->{return name.length();}).forEach(System.out::println); // map方法的Lambda表達式大括號中就是調用String類的length方法,可以使用方法引用 list.stream().map(String::length).forEach(System.out::println); } } -
小結:
1.首先分析Lambda表達式大括號中代碼(是否就是調用另一個方法\代碼和另一個方法一模一樣) 2.如果是,就可以使用方法引用替換Lambda表達式 3.確定引用的方法是什么類型(構造方法,靜態方法,沒有參數的成員方法,有參數的成員方法) 4.根據方法引用的格式去應用方法 構造方法: 類名::new 靜態方法: 類名::方法名 沒有參數的成員方法: 類名::方法名 有參數的成員方法: 對象名::方法名
5.3 Base64
Base64概述
- Base64是jdk8提出的一個新特性,可以用來進行按照一定規則編碼和解碼
Base64編碼和解碼的相關方法
-
編碼的步驟:
- 獲取編碼器
- 調用方法進行編碼
-
解碼步驟:
- 獲取解碼器
- 調用方法進行解碼
-
Base64工具類提供了一套靜態方法獲取下面三種BASE64編解碼器:
- 基本:輸出被映射到一組字符A-Za-z0-9+/,編碼不添加任何行標,輸出的解碼僅支持A-Za-z0-9+/。
- URL:輸出映射到一組字符A-Za-z0-9+_,輸出是URL和文件。
- MIME:輸出隱射到MIME友好格式。輸出每行不超過76字符,并且使用'\r'并跟隨'\n'作為分割。編碼輸出最后沒有行分割。
-
獲取編碼器和解碼器的方法
static Base64.Decoder getDecoder() 基本型 base64 解碼器。 static Base64.Encoder getEncoder() 基本型 base64 編碼器。 static Base64.Decoder getMimeDecoder() Mime型 base64 解碼器。 static Base64.Encoder getMimeEncoder() Mime型 base64 編碼器。 static Base64.Decoder getUrlDecoder() Url型 base64 解碼器。 static Base64.Encoder getUrlEncoder() Url型 base64 編碼器。 -
編碼和解碼的方法:
Encoder編碼器: encodeToString(byte[] bys)編碼 Decoder解碼器: decode(String str) 解碼
案例演示
- 基本
public class Test1 {
public static void main(String[] args) {
// 使用基本型的編碼器和解碼器對數據進行編碼和解碼:
// 1.獲取編碼器
Base64.Encoder encoder = Base64.getEncoder();
// 2.對字符串進行編碼
String str = "name=中國?password=123456";
String str1 = encoder.encodeToString(str.getBytes());
// 3.打印輸出編碼后的字符串
System.out.println("編碼后的字符串:"+str1);
// 4.獲取解碼器
Base64.Decoder decoder = Base64.getDecoder();
// 5.對編碼后的字符串進行解碼
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);
// 6.打印輸出解碼后的字符串
System.out.println("解碼后的字符串:"+str2);
}
}
- URL
public class Test2 {
public static void main(String[] args) {
// 使用URL型的編碼器和解碼器對數據進行編碼和解碼:
// 1.獲取編碼器
Base64.Encoder encoder = Base64.getUrlEncoder();
// 2.對字符串進行編碼
String str = "name=中國?password=123456";
String str1 = encoder.encodeToString(str.getBytes());
// 3.打印輸出編碼后的字符串
System.out.println("編碼后的字符串:"+str1);
// 4.獲取解碼器
Base64.Decoder decoder = Base64.getUrlDecoder();
// 5.對編碼后的字符串進行解碼
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);
// 6.打印輸出解碼后的字符串
System.out.println("解碼后的字符串:"+str2);
}
}
- MIME
public class Test3 {
public static void main(String[] args) {
// 使用MIME型的編碼器和解碼器對數據進行編碼和解碼:
// 1.獲取編碼器
Base64.Encoder encoder = Base64.getMimeEncoder();
// 2.對字符串進行編碼
String str = "";
for (int i = 0; i < 100; i++) {
str += i;
}
System.out.println("編碼前的字符串:"+str);
String str1 = encoder.encodeToString(str.getBytes());
// 3.打印輸出編碼后的字符串
System.out.println("編碼后的字符串:"+str1);
// 4.獲取解碼器
Base64.Decoder decoder = Base64.getMimeDecoder();
// 5.對編碼后的字符串進行解碼
byte[] bys = decoder.decode(str1);
String str2 = new String(bys);
// 6.打印輸出解碼后的字符串
System.out.println("解碼后的字符串:"+str2);
}
}
總結
必須練習:
1.Junit必須掌握--->@Test,@Before,@After,@BeforeClass,@AfterClass
2.反射獲取構造方法\成員方法\成員變量,并使用--->必須掌握,特別反射操作成員方法
3.使用注解(帶有屬性\不帶有屬性)=-->必須掌握
4.動態代理---->難點\重點\必須掌握
5.建議方法引用理解,Base64
- 能夠使用Junit進行單元測試
1.下載Junit的jar包
2.把Junit的jar包拷貝到模塊下的lib文件夾中,并添加到classpath路徑中
3.編寫測試方法
4.在測試方法上書寫@Test注解
5.運行測試
- 能夠通過反射技術獲取Class字節碼對象
1.1 類名.class
1.2 對象名.getClass();
1.3 Class.forName("類的全路徑");
- 能夠通過反射技術獲取構造方法對象,并創建對象。
Constructor getDeclaredConstructor(Class... parameterTypes)
Constructor[] getDeclaredConstructors()
T newInstance(Object... initargs)
void setAccessible(true);暴力反射
- 能夠通過反射獲取成員方法對象,并且調用方法。--------特別
Method getDeclaredMethod(String name,Class...args);
Method[] getDeclaredMethods();
Object invoke(Object obj, Object... args)
void setAccessible(true);暴力反射
- 能夠通過反射獲取屬性對象,并且能夠給對象的屬性賦值和取值。
Field getDeclaredField(String name);
Field[] getDeclaredFields();
void setAccessible(true);暴力反射
void set(Object obj, Object value)
Object get(Object obj)
- 能夠說出注解的作用
作為配置\編譯檢查
- 能夠自定義注解和使用注解
格式:
public @interface 注解名{
屬性
}
屬性格式:
數據類型 屬性名();
數據類型:
1.基本類型
2.String類型
3.Class類型
4.枚舉類型
5.注解類型
6.以上類型的一維數組類型
使用注解:
無屬性的注解: @注解名
有屬性的注解: @注解名(屬性名=屬性值,...)
注意事項:
1.如果注解屬性是數組類型,并且數組的值只有一個,那么大括號可以省略
2.如果注解只有一個屬性,并且屬性名為value,那么給屬性賦值的時候value屬性名可以省略
3.注解屬性有默認值,可以不給注解屬性賦值 格式: 數據類型 注解名() default 屬性值;
- 能夠說出常用的元注解及其作用
@Target:表示該注解作用在什么上面(位置),默認注解可以在任何位置
@Retention:定義該注解保留到那個代碼階段, 值為:RetentionPolicy類型,==默認只在源碼階段保留==
- 能夠解析注解并獲取注解中的數據
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等實現了該接口
- T getAnnotation(Class<T> annotationType):得到指定類型的注解引用。沒有返回null。
- boolean isAnnotationPresent(Class<?extends Annotation> annotationType):
判斷指定的注解有沒有。
獲取注解的屬性值: 注解對象.屬性名();
- 能夠完成注解的MyTest案例
1.獲取類的字節碼對象
2.獲取該類的所有方法
3.循環遍歷所有的方法
4.判斷遍歷出來的方法是否包含指定的注解,如果包含,就執行該方法
- 能夠說出動態代理模式的作用
作用: 為了增強被代理類的方法
- 能夠使用Proxy的方法生成代理對象
Java.lang.reflect.Proxy類可以直接生成一個代理對象
- public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一個代理對象
- 參數1:ClassLoader loader 被代理對象的類加載器
- 參數2:Class<?>[] interfaces 被代理對象的要實現的接口
- 參數3:InvocationHandler h (接口)執行處理類
- 返回值: 代理對象
- 前2個參數是為了幫助在jvm內部生成被代理對象的代理對象,第3個參數,用來監聽代理對象調用方法,幫助我們調用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:調用代理類的任何方法,此方法都會執行
- 參數1:代理對象(慎用)
- 參數2:當前執行的方法
- 參數3:當前執行的方法運行時傳遞過來的參數
- 返回值:當前方法執行的返回值
- 能夠使用四種方法的引用
總結:使用方法引用的步驟
1.分析要寫的Lambda表達式的大括號中是否就是調用另一個方法
2.如果是,就可以使用方法引用替換,如果不是,就不能使用方法引用
3.確定引用的方法類型(構造方法,成員方法,靜態方法,類的成員方法)
4.按照對應的格式去引用:
構造方法: 類名::new
成員方法(有參數): 對象名::方法名
靜態方法: 類名::方法名
類的成員方法\成員方法(無參數): 類名::方法名
- 能夠使用Base64對基本數據、URL和MIME類型進行編解碼
static Base64.Decoder getDecoder() 基本型 base64 解碼器。
static Base64.Encoder getEncoder() 基本型 base64 編碼器。
static Base64.Decoder getUrlDecoder() Url型 base64 解碼器。
static Base64.Encoder getUrlEncoder() Url型 base64 編碼器。
static Base64.Decoder getMimeDecoder() Mime型 base64 解碼器。
static Base64.Encoder getMimeEncoder() Mime型 base64 編碼器。
Encoder編碼器: encodeToString(byte[] bys)編碼
Decoder解碼器: decode(String str) 解碼
浙公網安備 33010602011771號