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.mzph.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;配置不拦截的路径 多个拦截器的执行顺序 过滤器和拦截器的区别 代码演示 …