java中函數式接口、Stream流、方法引用、junit單元測試、反射、注解

函數式接口:

在java中有且僅有一個抽象方法的接口稱為函數式接口,但是可以包含其它的默認的或靜態的方法。

格式:

修飾符 interface 接口名稱 {public abstract 返回值類型 方法名稱(可選參數);// 其他非抽象方法
}

函數式接口:

// 1.函數式接口:有且僅有一個抽象方法的接口,當然接口中可以包含其他默認、靜態、私有方法
// 2.為了確保接口是函數式接口,可以寫注解:@Functional Interface,它可以自動檢測是否為函數式接口,當有多個抽象方法時就會報錯。
@FunctionalInterface
public interface MethodsInterFace {public abstract void sayHi();// void eat();
}

接口實現類:

public class MethodsInterFaceTest implements MethodsInterFace {@Overridepublic void sayHi(){System.out.println("重寫了抽象方法sayHi");};
}

測試使用函數式接口(函數式接口作為參數使用):

public class Demo {// 1.函數式接口的使用:一般可以作為方法的參數和返回值類型:public static void testMethodsInterFace(MethodsInterFace mi){mi.sayHi();};public static void main(String[] args){// 創建一個接口實現類對象傳給testMethodsInterFace方法使用:MethodsInterFaceTest mit = new MethodsInterFaceTest();testMethodsInterFace(mit);// 2.可以在方法調用時直接傳遞接口的匿名內部類:testMethodsInterFace(new MethodsInterFace(){@Overridepublic void sayHi(){System.out.println("匿名內部類重寫了抽象方法sayHi");};});// 3.方法的參數是一個函數式接口時,可以使用Lambda表達式:Lambda表達式可以較匿名內部類節省內存,但是原理是不太一樣的,Lambda有延遲testMethodsInterFace(()->{System.out.println("使用Lambda表達式重寫了接口的抽象方法");});// 簡化Lambda表達式:testMethodsInterFace(()->System.out.println("使用簡化Lambda表達式重寫了接口的抽象方法"));};
}

函數式接口做飯返回值使用: 如果一個方法的返回值類型是一個函數式表達式,那么就可以直接返回一個Lambda表達式

import java.util.Arrays;
import java.util.Comparator;public class ComparatorDemo {// 1.實現一個方法,該方法返回java.util.Comparator接口類型作為字符串排序時使用(Comparator不僅僅可以用來做排序,它是一個比較器,比較靈活)public static Comparator<String> getSortResult(){//return new Comparator<String>(){//    @Override//    public int compare(String s1,String s2){//        // 按照字符串長度降序排序://        return s1.length() - s2.length();//    };//};// 方法返回一個函數式接口,可以使用Lambda簡化:return (s1,s2)-> s1.length() - s2.length();};public static void main(String[] args){String[] arr = {"123","0000","0"};System.out.println("1排序前的順序:" + Arrays.toString(arr)); // 1排序前的順序:[123, 0000, 0]Arrays.sort(arr,getSortResult());System.out.println("2排序后的順序:" + Arrays.toString(arr)); // 2排序后的順序:[0, 123, 0000]};
}

常用函數式接口簡介: JDK提供了大量常用的函數式接口以豐富Lambda的典型使用場景,它們主要在java.util.function包中被提供。

Supplier接口: 該接口包含一個無參數方法get,get返回一個前面泛型指定類型的數據 ,被稱為生產型接口,前面泛型指定什么數據類型,就會返回什么類型數據。

import java.util.function.Supplier;public class SupplierDemo {// 1.定義一個方法,方法參數傳遞一個Supplier<T>接口,泛型執行String,get方法就會返回一個Stringpublic static String getString(Supplier<String> sp){return sp.get();};// 2.練習:使用Supplier求數組元素中最大值:public static int getMaxNum(Supplier<Integer> sp){return sp.get();};public static void main(String[] args){// 1-1:方法的參數是一個函數式接口,可以使用lambda表達式:String str = getString(()-> "一個字符串");System.out.println(str); // 一個字符串// 2-1:定義一個int類型的數組:int[] arr = {1,5,2,3};int maxValue = getMaxNum(() -> {int maxTemp = arr[0];for (int i : arr) {if (maxTemp<i){maxTemp = i;};}return maxTemp;});System.out.println("數組中最大值:" + maxValue); // 數組中最大值:5};
}

Consumer接口: Consumer接口剛好與Supplier接口相反,Supplier接口接口用于生產一個數據,而Consumer用于消費一個數據,給一個指定類型的數據將這個數據使用掉。

import java.util.Locale;
import java.util.function.Consumer;public class ConsumerDemo {// 1.Consumer接口用于消費一個指定類型的數據,泛型指定什么類型,accept方法就消費什么類型的數據,具體怎么消費,需要自定義(打印,輸出,計算等)// 定義一個方法:方法的參數1傳遞一個字符串的姓名,方法的參數2傳遞Consumer接口消費字符串的姓名:public static void useName(String names, Consumer<String> cn){cn.accept(names);};// 2.Consumer接口的默認方法:andThen,andThen將多個Consumer組合起來再對數據進行消費:// 定義一個方法,方法傳遞自個字符串和兩個Consumer接口,接口泛型使用字符串:public static void useAndThen(String names, Consumer<String> cn1, Consumer<String> cn2){// cn1.accept(names);// cn2.accept(names);// 使用andThen代替上面方法:cn1.andThen(cn2).accept(names);};public static void main(String[] args){// 1.測試useNameuseName("kuhai123",cn -> {System.out.println(cn); // kuhai123// 翻轉字符串:鏈式編程多次調用String reNames = new StringBuffer(cn).reverse().toString();System.out.println(reNames); // 321iahuk});// 2.測試useAndThenuseAndThen("kuHai",cn1 -> System.out.println(cn1.toUpperCase()), cn2 -> System.out.println(cn2.toLowerCase()));};
}

Predicate接口:有時候需要對某種數據類型進行判斷,從而得到一個boolean值結果,這時候可以使用Predicate接口。

import java.util.function.Predicate;public class PredicateDemo {// 1.Predicate接口用于判斷數據是否滿足某個條件,返回布爾值,其中包含一個方法test做判斷:// 定義一個方法:參數傳遞一個字符串和一個Predicate接口,接口的泛型使用String,使用接口中的方法test對字符串進行判斷,并返回判斷結果:public static boolean isString(String str, Predicate<String> ps){return ps.test(str);};// 2.Predicate接口中有一個and方法,表示并且的意思:// 定義一個方法接收兩個Predicate接口和一個字符串,接口泛型指定為字符串,對字符串使用兩個接口做判斷,并返回判斷結果:public static boolean isAllSatisfy(String s, Predicate<String> p1, Predicate<String> p2){// return p1.test(s) && p2.test(s);return p1.and(p2).test(s);};// 3.Predicate接口中有一個or方法,表示或者的意思:// 定義一個方法接收兩個Predicate接口和一個字符串,接口泛型指定為字符串,對字符串使用兩個接口做判斷,并返回判斷結果:public static boolean isSomeSatisfy(String s, Predicate<String> p1, Predicate<String> p2){// return p1.test(s) || p2.test(s);return p1.or(p2).test(s);};// 4.Predicate接口中有一個negate方法,表示取反的意思:// 定義一個方法:參數傳遞一個字符串和一個Predicate接口,接口的泛型使用String,使用接口中的方法test對字符串進行判斷,并返回判斷結果:public static boolean isEmptyStr(String s, Predicate<String> p1){// return !p1.test(s);return p1.negate().test(s);};public static void main(String[] args){// 1.測試1:判斷字符串長度是否大于0:String tempStr = "abcde";// 調用方法做校驗:boolean rs = isString(tempStr,(s)->s.length() > 0);System.out.println(rs); // true// 2.測試2: 判斷字符串長度是否大于2并且小于5:String str2 = "123456";boolean s2 = isAllSatisfy(str2,(s)->s.length() > 2,(s)->s.length() < 5);System.out.println(s2); // false// 3.測試3: 判斷字符串長度是否小于5或大于8:String str3 = "1234";boolean s3 = isSomeSatisfy(str3,(s)->s.length() < 5,(s)->s.length() > 8);System.out.println(s3); // true// 4.測試4: 判斷字符串長是否為空字符串String str4 = "";boolean s4 = isEmptyStr(str4,(s)->s != "");System.out.println(s4); // true};
}

Function接口:用來根據一個類型的數據得到另一個類型的數據,前者稱為前置條件,后者稱為后置條件。

import java.util.function.Function;public class FunctionDemo {// 1.Function接口用來根據一個類型的數據得到另一個類型的數據,其中主要方法apply:// 定義一個方法將字符串轉換為Integer類型:public static void toNumber(String s, Function<String,Integer> f){// Integer n = f.apply(s);int n = f.apply(s); // 自動拆箱System.out.println(n); // 123};// 2.andThen方法用來進行組和操作:// 定義一個方法將字符串轉換為數字類型后加10后再轉換為字符串:public static void addTen(String str, Function<String,Integer> f1, Function<Integer,String> f2){String st = f1.andThen(f2).apply(str);System.out.println(st); // 20};public static void main(String[] args){// 1.測試:將字符串轉換為數字類型:String str = "123";toNumber(str,(String strs)->Integer.parseInt(strs));// 2.測試:將字符串加10后再返回:String s2 = "10";addTen(s2, st -> Integer.parseInt(st) + 10, n -> n + "");};
}

Stream流:

Strema流和io流是完全不一樣的兩種概念。Stream流用于對數組和集合做簡化操作。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;// Stream流式模型:當對一個數組或集合的多個元素進行操作時,可以先拼一個模型:filter過濾 -> 映射map -> 跳過skip -> 統計count
public class StreamDemo {// 1.對集合中的元素進行過濾處理,常用的方法就是遍歷處理,有的時候可能需要多個條件,此時有可能需要遍歷多次,這樣就會有點麻煩,此時可以使用Stream簡化處理:public static void main(String[] args){List<String> list = new ArrayList<>();list.add("張無忌");list.add("張一");list.add("張二二");list.add("李四");list.add("王五");// 2.集合中有一個方法stream,可以將集合轉換為Stream流:Stream流有個filter方法,找到滿足提交的數據,可以接收一個Lambda表達式,支持鏈式調用:list.stream().filter(s -> s.startsWith("張")).filter(s -> s.length() == 2).forEach(s -> System.out.println(s)); // 張一// 3.獲取流的方式:1.所有的Collection集合都可以通過stream默認方法獲取 2.Stream接口的靜態方法of獲取,of方法中接收一個數組Stream<Integer> ist = Stream.of(1,2,3,4);// 4.流模型的操作很豐富,常用api可分兩類:1.延遲方法(返回值類型仍然是Stream接口自身類型的api,支持鏈式調用)2.終結方法(返回值類型不再是Stream接口類型的api,調用終結方法后不再支持鏈式調用,終結方法常用的有:count/forEach)// 5.Stream流中forEach方法:該方法接收一個Consumer接口函數,會將每一個流元素交給該函數進行處理,Consumer接口是一個消費型的函數式接口,可以傳遞Lambda表達式,進行消費數據;簡單記憶:forEach遍歷流中的每一個元素,對每個元素進行處理,調用了forEach方法后就不能在調用Stream的其他方法了Stream<String> slist = Stream.of("趙麗穎","迪麗熱巴","霍元甲");slist.forEach(item -> System.out.println(item));// 6.Stream流中filter方法:filter方法將一個流轉換為另一個子集流,該方法接收一個Predicate函數式接口做為篩選條件,可對元素進行篩選:Stream<Integer> ilist = Stream.of(1,2,3,4);Stream<Integer> list2 = ilist.filter(item -> item > 3);list2.forEach(item -> System.out.println(item)); // 4// 7.Stream流的特點:Stream流屬于管道流,只能被消費一次,使用一次就不能被使用了,第一個Stream流調用完畢后就會流到下一個Stream上,而此時第一個Stream流已經使用完畢了,就會被關閉,再使用就會報錯:// list2.forEach(item -> System.out.println(item)); // 拋出了異常// 8.Stream流中map方法:map將一個流中的元素映射到另一個流中,該方法接收一個Function接口,使用Function接口可將某個類型轉換為另一個類型,做依依映射:Stream<Integer> isst = Stream.of(1,2,3,4);Stream<String> isst2 = isst.map(item -> item.toString());isst2.forEach(item -> System.out.println(item));// 9.Stream流中提供了count方法:count用于統計流中元素的個數,類似Collection當中的size,返回值類型為long類型,該方法是一個終結方法:Stream<Integer> listl = Stream.of(1,2,3,4);System.out.println(listl.count()); // 4// listl.forEach(item -> System.out.println(item)); // 使用過了,再使用會拋異常// 10.Stream流中的limit方法:limit方法用于截取前n個元素,n類型是long,返回的是新的流,支持鏈式調用:String[] arrs = {"元素1","元素2","元素3","元素4","元素5",};Stream<String> streamlist = Stream.of(arrs);Stream<String> streamlist2 = streamlist.limit(2);streamlist2.forEach(item -> System.out.println(item)); // 元素1 元素2System.out.println("-------------------");// 11.Stream流中的skip方法:skip方法用于跳過前n個元素返回剩下的元素,n類型是long,返回的是新的流,支持鏈式調用:(當傳入的參數大于元素的個數時會得到一個長度為0的空流)String[] arrs2 = {"元素1","元素2","元素3","元素4","元素5",};Stream<String> ster = Stream.of(arrs2);Stream<String> ster2 = ster.skip(2);ster2.forEach(item -> System.out.println(item)); // 元素3 元素4 元素5// 12.Stream流中的concat靜態方法:concat方法用于將兩個流合并成一個流:Stream<String> l1 = Stream.of("1","2");Stream<String> l2 = Stream.of("3","4");Stream<String> l3 = Stream.concat(l1,l2);l3.forEach(item -> System.out.println(item)); // 1 2 3 4};
}

方法引用:

方法引用實際是對Lambda的優化,如:

Printable接口:

// 1.定義一個打印的函數式接口:
@FunctionalInterface
public interface Printable {// 打印字符串的抽象方法:void print(String s);
}
public class PrintDemo {// 2.定義一個方法傳遞Printable接口,對字符串進行打印:public static void printString(Printable p){p.print("打印內容:");};public static void main(String[] args){// 3.調用printString方法:printString(s -> System.out.println(s)); // 打印內容:// 3-1方法引入調用方法:Lambda表達式的目的,打印參數傳遞的字符串,把參數s傳遞給了System.out對象,調用out對象中的方法println對字符串輸出,注意:1.System.out對象已經存在 2.println方法也已經存在,所以可以使用方法引入優化Lambda表達式(可以使用System.out直接引入方法println),如:printString(System.out::println); // 打印內容:// ::被稱為引用運算符,而它所在的表達式被稱為方法引用,如果Lambda要表達的函數方案已經存在于某個方法的實現中,那么可以使用雙冒號來引用該方法作為Lambda的代替:};
}

通過對象名引用成員方法:

定義一個包含成員方法的類:

public class MethodsRerObject {// 1.定義一個成員方法,傳遞字符串,把字符串按照大寫輸出:public void printUpperCaseString(String str){System.out.println(str.toUpperCase());};
}

通過對象名引用方法測試:

public class MethodsReferenceDemo {// 2.通過對象名引用成員方法:使用前提對象名是已經存在的,成員方法也是已經存在的,就可以是使用對象名來引用成員方法:// 定義一個方法,方法的參數傳遞Printable接口:public static void printString(Printable p){p.print("asda");};public static void main(String[] args){printString((s) -> {// 3.創建一個MethodsRerObject對象:MethodsRerObject obj = new MethodsRerObject();// 4.調用對象中成員方法:按照大寫輸出obj.printUpperCaseString(s); // ASDA});// 方法引用優化:MethodsRerObject obj = new MethodsRerObject();printString(obj::printUpperCaseString); // ASDA};
}

通過類名稱引用靜態方法:

@FunctionalInterface
public interface Calcable {// 1.定義一個抽象方法:傳遞一個整數,對整數進行絕對值計算:int calsAbs(int n);
}
public class StaticClassMethodsReferenceDemo {// 2.通過類名稱引用靜態成員方法:前提類已存在,靜態方法已經存在// 定義一個方法,方法的參數傳遞要計算絕對值的整數和Calcable接口public static int methodsabs(int num, Calcable c){return c.calsAbs(num);};public static void main(String[] args){// 3.調用methodsabs方法:int rs = methodsabs(-5,cn -> Math.abs(cn));System.out.println(rs); // 5// 優化:通過類引用靜態方法:int rs2 = methodsabs(-5,Math::abs);System.out.println(rs2); // 5};
}

通過super引用成員方法: 如果在繼承關系中,當Lambda中需要出現super調用時,也可以使用方法引用進行代替,如:

@FunctionalInterface
public interface Geetable {void greet();
}
// 定義一個父類:
public class Human {// 定義一個方法:public void sayHai(){System.out.println("hi,我是human");};
}
// 定義子類,繼承Human:
public class Man extends Human {// 子類重寫sayHai方法:@Overridepublic void sayHai(){System.out.println("hi,我是Man");};// 定義一個方法,參數是Greetable接口:public void method(Geetable g){g.greet();};// 定義一個show方法:在show方法中調用method接口:public void show(){// method(() -> {//     // 創建父類對象://     Human hm = new Human();//     // 調用父類的syaHi方法://     hm.sayHai(); // hi,我是human// });// 優化:因為有子父類關系,所以存在一個關鍵字super,代表父類,所以我們可以直接使用super調用父類的成員方法:// method(() -> super.sayHai());method(super::sayHai);};public static void main(String[] args){// 創建Man對象:并調用show方法:new Man().show();};
}

通過this引用成員方法: this代表當前對象,如果需要引用的方法,

// 定義一個富有的函數接口:
public interface Richable {// 購買的方法:void buy();
}
public class Husband {// 定義一個買房子的方法:public void buyHouse(){System.out.println("買房");};// 定義一個買車的方法,參數傳遞Richable接口:public void buyCar(Richable r){r.buy();//  System.out.println("買車");};// 定義一個非常高興的方法:public void soHappy(){// 調用買車的方法:// buyCar(() -> {//     this.buyHouse();// });// 優化:buyCar(this::buyHouse);};public static void main(String[] args) {new Husband().soHappy();}
}

類的構造器引用: 由于構造器的名稱和類名稱完全一樣,并不固定,所以構造器引用使用類名稱::new 的格式表示,如:

// 定義一個類:
public class Person {private String name;public String getName() {return name;}public Person() {}public Person(String name) {this.name = name;}public void setName(String name) {this.name = name;}
}
// 定義一創建Person對象的函數式接口:
@FunctionalInterface
public interface PersonBuilder {// 定義一個方法,根據傳遞的姓名,創建Person對象:Person builderPerson(String name);
}
// 定義一個測試demo
public class Demo {// 定義一個方法,傳遞姓名和PersonBuilder接口,方法中通過姓名創建對象:public static void printName(String name,PersonBuilder p){Person ps = p.builderPerson(name);System.out.println(ps);};public static void main(String[] args) {// 調用printName方法:// printName("苦海123", (String name) -> {//     return new Person(name);// });// 優化:使用方法引用:printName("苦海123", Person::new);}
}

數組構造器引用:

數組也是Object的子類對象,所以同樣具有構造器,只是語法對應到Lambda的使用場景中時,需要一個函數式接口:

// 定義一個創建數組的函數式接口:
@FunctionalInterface
public interface ArrayBuilder {// 定義創建int類型的數組的方法,參數傳遞數組的長度,返回創建好int類型的數組:int[] builderArray(int length);
}
public class ArrayBuildDemo {// 定義一個方法,方法的參數傳遞創建數組長度和ArrayBuilder接口,方法內部根據傳遞的長度使用ArrayBuild中的方法創建數組并返回:public static int[] createArray(int length,ArrayBuilder ab){return ab.builderArray(length);};public static void main(String[] args) {// 調用createArray方法:// int[] arr1 = createArray(5,(len) -> {//     return new int[len];// });// 優化:使用數組構造器引用:int[] arr1 = createArray(5,int[]::new);System.out.println(arr1.length);}
}

junit單元測試:

測試大概可以分為兩類:

黑盒測試:不需要寫代碼,給輸入值,看程序最終能否輸出想要的結果。

白盒測試:需要寫代碼,關注程序的具體執行流程。

junit使用步驟:1.定義一個測試類(測試用例,類名推薦XXXTest,放在XXX.XXX.test包)2.定義測試方法:可獨立運行,方法名推薦testXXX 3.給方法加@Test注解

import org.junit.After;
import org.junit.Before;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;public class CalculateTest {// 1.加測試注解@Testpublic void testAddnum(){// 2.創建需要測試累的對象:Calculate cs = new Calculate();// 3.調用對象的方法:int result = cs.addNum(5,3);System.out.println(result);// 點擊編輯器左側箭頭即可執行此方法,無需寫main函數,點哪個箭頭,對應的函數會執行,如果方法有異常拋出,那么控制臺會有爆紅警告,報紅表示測試失敗// 4.斷言:上面爆紅只是語法上的錯誤,真要測試一個結果是否正確,那么需要借助Assert下的相關方法對輸出的結果和想要的結果進行比較:Assert.assertEquals(8,result); // 當輸出的結果和想要的結果不一樣時,這里也會報紅};// 2.初始化方法:用于申請資源等,加@Befoure注解,執行于@test前@Beforepublic void init(){System.out.println("Before注解方法執行");};// 3.釋放資源方法:所有測試方法都執行完后會自動執行的方法,在前面加@After注解即可,執行于@test后@Afterpublic void destory(){System.out.println("After注解方法執行");};
}

反射:

將類的各個組成部分封裝為其他對象,這就是反射機制,反射的好處:可以在程序運行過程中操作這些對象,可以解耦,降低程序的耦合性,提高程序的可擴展性。

Person類文件:

public class Person {private String name;private int age;public String grad;public Person(String name, int age, String grad) {this.name = name;this.age = age;this.grad = grad;}public Person() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getGrad() {return grad;}public void setGrad(String grad) {this.grad = grad;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", grad='" + grad + '\'' +'}';}public void eat(){System.out.println("吃...");};public void eat(String foot){System.out.println("吃..." + foot);};
}

pro.properties配置文件:

# 1.配置文件:
# 定義一個類名:這里的類是項目中存在的Class文件
className=Person
# 定義一個方法名:這里的方法也是Class類文件中對應出現的方法
methodName=eat

測試反射技術:

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;public class ReflectDemo1 {// 獲取class對象的方式:1.Class.forName("全類名"),將字節碼文件加載進內存,返回class對象 2.類名.class,通過類名的屬性class獲取 3.對象.getClass(),通過Object類中定義的方法getClass獲取public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException, IOException {// 1.Class.forName("全類名"),將字節碼文件加載進內存,返回class對象,多用于配置文件Class cls1 = Class.forName("Person");System.out.println(cls1); // class Person// 2.類名.class,通過類名的屬性class獲取,多用于參數傳遞Class cls2 = Person.class;System.out.println(cls2); // class Person// 3.對象.getClass(),通過Object類中定義的方法getClass獲取,多用于對象獲取字節碼的方式Person p1 = new Person();Class cls3 = p1.getClass();System.out.println(cls3); // class PersonSystem.out.println(cls1 == cls2 && cls2 == cls3); // true,同一個.class字節碼文件在一次程序運行過程中只被加載進內存一次。System.out.println("-----------------------");// class對象功能:1.獲取所有的成員變量 2.獲取所有的構造方法 3.獲取所有成員方法 4.獲取類名// 獲取Person的class對象:Class classObj = Class.forName("Person");// 1-1.獲取所有被public修飾的成員變量: getFields用于獲取public修飾的成員變量Field[] flist = classObj.getFields();for (Field item : flist) {System.out.println(item); // 這里獲取到的結果:public java.lang.String Person.grad}// 1-2.獲取指定被public修飾的成員變量: getField用于獲取指定名稱的被public修飾的成員變量Field fie = classObj.getField("grad");System.out.println(fie); // 這里獲取到的結果:public java.lang.String Person.grad// 1-3.獲取成員變量的作用:可以獲取和設置成員變量對應的值:get用于獲取成員變量的值,get方法接收一個對象實例 set用于設置成員對象的值,接收兩個變量,一是對象實例,二是成員變量對應要設置的值Person p2 = new Person();fie.set(p2,"10班");Object p = fie.get(p2);System.out.println(p); // null,初識化為null值,前面加了set,所以加set后的值為10班// 1.4獲取所有的成員變量:getDeclaredFields()獲取所有的成員變量,不被修飾符限制Field[] flists = classObj.getDeclaredFields();for (Field item : flists) {System.out.println(item); // 這里獲取到的結果:private java.lang.String Person.name 、 private int Person.age 、 public java.lang.String Person.grad}// 1-5.獲取指定的成員變量:getDeclaredField()獲取指定名稱的成員變量,不被修飾符鎖限制,即使是私有的也是可以被操作的,但是會拋異常,只要使用setAccessible忽略異常就可以正常運行Field fits = classObj.getDeclaredField("name");fits.setAccessible(true); // 獲取訪問權限修飾符的安全檢查:true為忽略,false為不忽略Person p3 = new Person("苦海",18,"11班");fits.set(p3,"kuhai123");System.out.println(fits.get(p3)); // 原本是:苦海,但是前面重新設置了kuhai123,所以這里打印:kuhai123// 2-1.獲取對象構造器:getConstructor用來獲取構造器函數,根據可變參數獲取對應的構造器:Constructor cn = classObj.getConstructor(String.class, int.class,String.class);System.out.println(cn);// 構造方法的作用:用來創建對象Object p4 = cn.newInstance("苦海123",16,"6班");System.out.println(p4.toString()); // Person{name='苦海123', age=16, grad='6班'}// 3-1.獲取方法:getMethod用來獲取指定名稱的方法:Method eats = classObj.getMethod("eat",String.class); // 第一個參數為方法名,后面可接收對應類型重載方法// 獲取方法的作用:使用invoke調用方法:Person p5 = new Person();eats.invoke(p5,"蘋果"); // 吃...被打印了,如果獲取方法時傳遞了對應參數的重載方法,則第二個參數開始為重載方法所需的參數,傳遞了參數的值:吃...蘋果// 3-2.獲取所有public修飾的方法:Method[] methods = classObj.getMethods();for (Method item : methods) {System.out.println(item); // 這里獲取到的結果:這里除了對象自身的方法外,還有繼承于Object的一些方法,getName()可獲取方法名稱:System.out.println(item.getName());}// 方法獲取到也是可以執行的,通過invoke(接收多個對象)System.out.println("------------******--------------");// 案例:實現一個可以定義任意類和執行該類的任意方法的框架:// 實現步驟:1.將要創建的對象的全類名和所需要執行的方法定義在配置文件中 2.在程序中加載讀取配置文件 3.使用反射技術來加載類文件進內存 4.執行方法// 1.在項目包文件夾下定義一個:pro.properties配置文件,文件名可以自定義,但是后綴是:.properties結尾,定義好配置文件后,可以通過以下方法加載文件:// 1-1.創建Properties對象Properties pro = new Properties();// 1-2.調用pro的load方法加載配置文件到內存,并將其轉換為一個雙鏈集合:// 1-2-1.通過getClassLoader()獲取類字節碼文件的加載器:ClassLoader classLoader = ReflectDemo1.class.getClassLoader();// 1-2-2.可以借助ClassLoader里面的getResourceAsStream方法獲取資源對應的字節流,這里和使用io流是一個意思,但是io獲取就比較麻煩了InputStream isStrem = classLoader.getResourceAsStream("pro.properties");// 1-2-3.通過load方法加載字節流:pro.load(isStrem);// 2.獲取配置文件中定義的數據:String className = pro.getProperty("className");String methodName = pro.getProperty("methodName");// 3.加載該類進內存,返回一個Class對象:Class cls = Class.forName(className);// 4.創建對象:newInstance方法已被啟用Object objs = cls.newInstance();// 5.獲取方法對象:Method methObj = cls.getMethod(methodName);// 6.執行方法:methObj.invoke(objs);// 以上創建對象并執行對象方法的優勢:可以不用改代碼,只是修改配置文件就可以讓一個新的類創建對象并調用其中的方法,不用再去測試代碼是否有bug,項目龐大時,可擴展性強}
}

注解:

注解也叫元數據,它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查,如Override;通過代碼里的注解生成doc文檔;對代碼進行分析等。說白了就是在寫代碼的時候加上特殊的注釋,可以通過java相關命令自動生成開發文檔以及代碼的自校驗等

生成開發文檔:通過命令:javadoc 類文件名 ,可以生成html說明文檔,如果報錯,則修改編碼為GBK即可

JDK預定義的一些注解:

@Override:檢測被該注釋標注的方法是否是繼承自父類的。

@Deprecated:該注解表示已過時,表示某個內容已經過時,但是可以使用。

@SuppressWarnings:壓制警告。

自定義注解:

自定義注解分兩部分:1.元注解,就是寫給自己的注釋,可以不寫 2.寫個程序的注解,真正生效的注解,其格式:public @interface 注解名稱 {}

注解本質:public interface MyAnno extends java.lang.annotation.Annotation {},可以看出實際就是一個接口,此接口中可以定義屬性(接口中的抽象方法,必須有返回值,返回值類型:基本類型、枚舉、注解)

元注解定義的一些注釋:

@Target:描述注解所作用的位置

@Retention:描述注解被保留的階段

@Documented:描述注解是否被抽取到api文檔中

@Inherited:注解是否被子類繼承

定義一個注解:

// 1.定義一個自定義注解MyAnno:(通過反編譯可以查詢自定義注解的源碼,反編譯步驟:1.先javac編譯注解java文件 2.javap編譯注解class字節碼文件),通過反編譯得到的結果:public interface MyAnno extends java.lang.annotation.Annotation {},注解本質就是一個接口
// 元注解定義:import java.lang.annotation.*;
@Target(value={ElementType.METHOD,ElementType.TYPE}) // ElementType的值有:TYPE表示MyAnno注解只能注解在類上、METHOD可以作用域方法上、FIELD可以作用于成員變量上,多個類型可以同時添加,只需要用逗號隔開即可。
@Retention(RetentionPolicy.RUNTIME) // 這里也是一個枚舉,但是一般自己定義的注解一般使用RUNTIME即可,表示當前秒數的注解會保留到class字節碼文件中并被jvm讀取到、 如果設置為CLASS則表示注釋會保留到class字節碼文件中,但不會被jvm讀取到
@Documented // 加此注解表示當前的注解會被加載到api文檔中
@Inherited // 加此注解表示子類繼承該注解
public @interface MyAnno {public String show(); // 這里的抽象方法也可以叫做屬性,方法的返回值是有要求的:基本類型、枚舉、注解int show1() default 0; // 使用default可以給默認值,如果不給默認值,在使用注解時就要給值
}

在Person類中使用自定義類:

public class Person {public String name;// 2.使用自己定義的注解:后面加括號可以傳值給注解,多個值用逗號隔開,如果不想賦值,那么定義的時候就要給默認值:@MyAnno(show = "hello", show1 = 8) // 如果注解只有一個抽象方法,并且抽象方法名稱為value時,這里使用注解時可以直接傳值,如:@MyAnno(8)public void sayHi(){System.out.println("hi...");};
}

使用注解實現利用反射自動創建對象并調用對象的方法:

定義一個注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 定義一個描述執行的類名和方法名的注解demo:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {String className();String methodName();
}

定義一個類:

public class DemoClass {public void show(){System.out.println("show...");};
}

實現過程:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;// 用注解代實現框架創建對象案例:
@Pro(className="DemoClass",methodName="show")
public class ReflectDemo {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InstantiationException {// 1.解析注釋:// 1-1.獲取該類字節碼文件對象:Class<ReflectDemo> refcls = ReflectDemo.class;// 1-2.獲取上邊的注釋對象:Pro ans = refcls.getAnnotation(Pro.class); // 在內存中生成了該注釋接口的子類實現類對象// 1-3.調用注解對象中定義的方法獲取返回值:String className = ans.className();String methodName = ans.methodName();// 2.加載該類進內存,返回一個Class對象:Class cls = Class.forName(className);// 3.創建對象:newInstance方法已被啟用Object objs = cls.newInstance();// 4.獲取方法對象:Method methObj = cls.getMethod(methodName);// 5.執行方法:methObj.invoke(objs);};
}

提示:本文圖片等素材來源于網絡,若有侵權,請發郵件至郵箱:810665436@qq.com聯系筆者刪除。
筆者:苦海

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35639.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35639.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35639.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

服務器安全維護注意事項有哪些?

服務器的安全關系著公司整個網絡以及所有數據的安全&#xff0c;我們該如何做好服務器后續的安全維護呢?河南億恩科技股份有限公司&#xff0c;專注服務器托管23年&#xff0c;不僅是國內專業的互聯網基礎應用服務提供商之一&#xff0c;還是國家工信部認定的綜合電信服務運營…

OpenJDK Maven 編譯出錯: package jdk.nashorn.internal.runtime.logging does not exist

前言 OpenJDK 1.8.0Maven 3.8.5TencentOS Server 3.1 錯誤信息 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project tour-common: Compilation failure: Compilation failure: [ERROR] /opt/tour-c…

JavaScript預編譯機制

變量預編譯 任何變量&#xff0c;如果未經聲明就賦值&#xff0c;此變量是屬于 window 的屬性&#xff0c;而且不會做變量提升&#xff0c;無論在哪個作用域內賦值。比如說直接寫 console.log(a)肯定會報錯&#xff0c;提示找不到 a。但如果直接寫 a 100就不會報錯&#xff0…

【Linux命令行與Shell腳本編程】第十九章 正則表達式

Linux命令行與Shell腳本編程 第十九章 正則表達式 文章目錄 Linux命令行與Shell腳本編程 第十九章 正則表達式九.正則表達式9.1.正則表達式基礎9.1.1.正則表達式的類型9.2.定義BRE模式9.2.1.普通文本9.2.2.特殊字符 9.2.3.錨點字符錨定行首^錨定行尾$組合錨點 9.2.4.點號字符\.…

funbox3靶場滲透筆記

funbox3靶場滲透筆記 靶機地址 https://download.vulnhub.com/funbox/Funbox3.ova 信息收集 fscan找主機ip192.168.177.199 .\fscan64.exe -h 192.168.177.0/24___ _/ _ \ ___ ___ _ __ __ _ ___| | __/ /_\/____/ __|/ __| __/ _ |/ …

SpringBoot復習(39)Servlet容器的自動配置原理

Servlet容器自動配置類為ServletWebServerFactoryAutoConfiguration 可以看到通過Import注解導入了三個配置類&#xff1a; 通過這個這三個配置類可以看出&#xff0c;它們都使用了ConditionalOnClass注解&#xff0c;當類路徑存在tomcat相關的類時&#xff0c;會配置一個T…

【數據結構?堆】序列和的前n小元素

題目描述 問題&#xff1a;序列和的前n小元素   給出兩個長度為n的有序表A和B, 在A和B中各任取一個, 可以得到 n^2 個和. 求這些和最小的n個。 輸入輸出格式 輸入格式&#xff1a; 輸入數據共三行。   第一行&#xff0c;一個整數值n &#xff08; n < 10^4 &#xff…

Linux系列:從0到1用Docker部署springboot項目

目錄 1.前提條件 2.編寫DockerFile鏡像文件 3.打包SpringBoot項目 4.通過軟件Xftp進行傳輸&#xff08;*&#xff09; 1.點擊“文件-新建”?編輯 5.操作遠程主機 1.docker構建 2.容器運行 6.容器的關閉和刪除 1.前提條件 Linux、docker、xftp的安裝、一臺可以訪問的遠…

教雅川學纏論07-中樞實戰眾泰汽車000980

本文實戰眾泰汽車 下面是2023年11月14-2023年8月8眾泰汽車日K圖 先畫日K 接下來處理包含&#xff0c;就變成下面這個樣子 下面在套上纏論的理論&#xff0c;未來股價的走勢應該是紅色橢圓形虛線里面的樣子 好了&#xff0c;文章就到這里&#xff0c;如果眾泰最終不是這個走勢…

linux 目錄操作命令

目錄操作命令 文件列表 ls命令文件列表 ls [選項] [參數]-------------------------------l 詳細信息-L 緊接著符號性連接&#xff0c;列出它們指向的文件-a 所有文件&#xff0c;包含隱藏文件(以點號起始的文件)-A 與-a相同&#xff0c;但是不會列出來. 和 ..-c 根據創建時間排…

IDEA部署配置Maven項目教程,IDEA配置Tomcat(2019.3.3)

一、前言 當涉及到軟件開發和項目管理時&#xff0c;使用一個可靠的構建工具是非常重要的。Maven是一個廣泛使用的構建工具&#xff0c;它為Java項目提供了一種簡化的構建過程和依賴管理。 在本文中&#xff0c;我們將探討如何部署Maven并開始使用它來構建您的項目。我們將介紹…

Java基礎篇--淺拷貝和深拷貝

概念 淺拷貝&#xff08;Shallow Copy&#xff09;和深拷貝&#xff08;Deep Copy&#xff09;是在對象復制過程中常用的概念。 淺拷貝是指創建一個新對象&#xff0c;并將原始對象的非靜態字段的值拷貝到新對象中。如果字段是基本數據類型&#xff0c;直接復制其值&#xf…

開源數據庫Mysql_DBA運維實戰 (修改root密碼)

MySQL——修改root密碼的4種方法 本文以windows為例為大家詳細介紹下MySQL修改root密碼的4種方法&#xff0c;大家可以可以根據的自己的情況自由選擇&#xff0c;希望對大家有所幫助 方法1&#xff1a; 用SET PASSWORD命令 首先登錄MySQL。 格式&#xff1a;mysql> set pass…

Android APK體積優化(瘦身)

1、基礎知識&#xff1a; 1.1 apk結構 lib &#xff1a;存放so文件&#xff0c;對應不同的cpu架構 res &#xff1a;資源文件&#xff0c;layout、drawable等&#xff0c;經過aapt編譯 assets &#xff1a;資源文件&#xff0c;不經過aapt編譯 classes.dex &#xff1a;dx編譯…

爬蟲:使用Selenium模擬人工操作及獲取網頁內容

專欄介紹 結合自身經驗和內部資料總結的Python教程,每天3-5章,最短1個月就能全方位的完成Python的學習并進行實戰開發,學完了定能成為大佬!加油吧!卷起來! 全部文章請訪問專欄:《Python全棧教程(0基礎)》 再推薦一下最近熱更的:《大廠測試高頻面試題詳解》 該專欄對…

graphab 教程 ——生成廊道

Graphab軟件包括圖譜創建、基于圖譜的連通性計算、分析與推廣、制圖四個模塊。Graphab軟件的圖譜創建基于柵格數據進行,包括斑塊識別和連接建立兩個步驟。Graphab 軟件可識別的柵格數據格式包括TIFF、ASCI和RST,柵格像元記錄數值用于識別斑塊類型,識別規則可以選擇四鄰域或八鄰…

2-redis單節點搭建安裝

1.系統要求 本次redis四種模式(單機(standalone)模式、主從(master-slave)模式、哨兵(sentinel)模式、集群(cluster)模式)的搭建,以CentOS服務器進行。 類型版本CentOS7.9Redis7.0.121.1.OS基礎配置 CentOS為了能夠正常安裝redis,需要對CentOS進行常規的一些基礎配置,主要…

【Zabbix安裝-5.5版本】

Zabbix安裝&#xff08;rpm包安裝&#xff09; Index of /zabbix/zabbix/5.5/rhel/8/x86_64/ | 清華大學開源軟件鏡像站 | Tsinghua Open Source Mirror rpm包鏈接&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/5.5/rhel/8/x86_64/zabbix-release-5.5-1.e…

Unity3d C#利用本地網頁快速打開螢石云監控視頻流(ezopen)實現云臺,聲音等控制,支持WebGL平臺,替代UMP播放(含源碼)

前言 之前我介紹了替代Universal?Media?PlayerUMP播放石云監控視頻流(ezopen)的功能&#xff0c;效果還是很明顯的&#xff0c;筆者的測試是差不多3-5秒就能打開監控畫面&#xff0c;不過稍微遺憾的是&#xff0c;之前的功能是iframe打開石云提供的播放網頁的形式&#xff0…

詳解攔截器和過濾器

目錄 代碼演示過濾器Demo攔截器Demo 過濾器自定義攔截器配置攔截器過濾器執行原理多個過濾器的執行順序 攔截器自定義攔截器注冊攔截器1&#xff09;注冊攔截器2&#xff09;配置攔截的路徑3&#xff09;配置不攔截的路徑 多個攔截器的執行順序 過濾器和攔截器的區別 代碼演示 …