目录
一、反射的概念
1.定义
2.作用
二、反射操作的四个核心类(java.long.reflect包下)
1.Class:反射操作的核心类
1)java中获取一个类的class对象一共有三种途径:
2)总结:要想通过反射操作类或者对象,第一步就要获得该类的class对象
3)高阶语言中有两种类型:编译器类型和运行时类型(RTTI)
4)通过反射来创建一个类的实例:
2.Constructor:与构造方法相关的类
1)getConstructors与getDeclaredConstructors:获取构造方法
2)获取特定一个构造方法
3)当拿到特定的Constructor对象时,就可以通过该对象来实例化Student对象
3.Method:与类中方法相关的类
1)getMethods和getDeclaredMethods
2)拿到Method对象后,通过反射来调用对象的方法
3)总结:通过反射调用一个类的方法的步骤
4.Field:描述一个类的属性的反射操作类
1)getFields和getDeclaredFields
3)通过反射来调用对象的属性
3)总结:通过反射调用一个类的属性的步骤(Field的使用)
三、Lambda表达式
1.概念
2.匿名内部与Lambda表达式简化接口实现子类过程
1)任何能使用Lambda表达式的前提:接口只有一个抽象方法
2)@FunctionalInterface注解
3.Lambda表达式的使用
1)Lambda表达式语法
2)对于接口的抽象方法分为以下几种情况:(不同情况Lambda表达式的使用)
4.Lambda表达式作用
一、反射的概念
1.定义
反射是所有第三方框架的基础。动态获取类信息以及动态调用该对象的属性和方法的功能称之为反射(此处的动态,指的是运行时获取,即javac生成class文件,当java运行一个主类时,反射就存在于运行时)。
2.作用
反射的作用就是在运行时,对于任意一个类,都能拿到该类的所有属性和方法信息【IDEA中,我们可以获取任意一个类的所有方法和属性】;对于任意一个对象来说,都能调用该对象的任意属性和方法。
二、反射操作的四个核心类(java.long.reflect包下)
Class:反射操作的核心类(区别class,class是一个声明类的关键字)
Constructor:与构造方法相关的类
Method:与类中方法相关的类
Field:与类中属性相关的类
1.Class:反射操作的核心类
javac命令:源文件.java(如javac Test.java)->生成Test.class文件【这个class二进制文件就是编译后的文件,给JVM阅读,包含了编译后该类的所有信息】->运行:java Test【这个命令实际就是把Test.class这个文件加载到JVM进程中,从其中的主方法开始执行】
Class类:每当使用javac编译一个源文件,加载到JVM的时候,每个类就对应一个Class对象,这个对象由JVM生成,一个类只有一个Class对象。
1)java中获取一个类的class对象一共有三种途径:
1.直接通过类名称.class获取
2.通过该类的任意实例化对象,调用getClass方法
3.使用Class类提供的Class.forName("类的全名称")
public class Student extends Person{
public class Person {
}
public class ReflectTest {
public static void main(String[] args) throws Exception {
//获取Student类的class对象-3种方法
//1.直接通过类名称.class
Class cls1=Student.class;
//2.通过Student类的任意一个实例化对象,调用它的getClass方法
Class cls2=new Student().getClass();
//3.通过Class的forName方法
//此处要传入类的全名称:包名.类名
Class cls3=Class.forName("java_reflect.Student");
}
}
一个类只有一个Class对象,所以说明cls1、2、3是一个东西,证明:
2)总结:要想通过反射操作类或者对象,第一步就要获得该类的class对象
有了这个对象以后才能通过反射去操作(class对象就是用来描述这个Student类到底具备了哪些属性和方法)。
3)高阶语言中有两种类型:编译器类型和运行时类型(RTTI)
eg:向上转型中:Person per1=new Student();per1这个引用编译期类型就是Person,最终在JVM到底是哪个类型就是运行时类型。
4)通过反射来创建一个类的实例:
先获取该类的class对象;再调用class对象的newInstance()方法默认调用该类的无参构造;当无参构造不存在或者无参构造不可见(私有或者不同包),这个方法就无法使用。
public class Student extends Person{
private String name;
public int age;
String country;
public Student(){
System.out.println("Non Parameter");
}
public Student(String name) {
this.name = name;
}
public Student(String name, int age, String country) {
this.name = name;
this.age = age;
this.country = country;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", country='" + country + ''' +
'}';
}
}
注:构造方法私有时或没有时,调用就会出错。这时候就引出了Constructor类(专门和构造方法打交道)。
2.Constructor:与构造方法相关的类
当需要使用类的其他构造方法来创建对象时,就需要用到这个和构造方法相关的反射类。
1)getConstructors与getDeclaredConstructors:获取构造方法
Class类的getConstructors只能获取当前类的所有public权限的构造方法
Class类的getDeclaredConstructors能获取当前类的所有权限的构造方法
但是getDeclaredConstructors与getConstructors都无法获取到父类的Constructor对象
package java_reflect;
public class Student extends Person{
private String name;
public int age;
String country;
private Student(){
System.out.println("Non Parameter");
}
Student(String name) {
System.out.println("One Parameter");
this.name = name;
}
public Student(String name, int age, String country) {
System.out.println("three Parameters");
this.name = name;
this.age = age;
this.country = country;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", country='" + country + ''' +
'}';
}
}
package java_reflect;
public class Person {
private String personName;
public int personAge;
public Person(){
System.out.println("Person的无参构造");
}
private Person(String personName){
System.out.println("Person的有参构造");
this.personName=personName;
}
}
2)获取特定一个构造方法
//拿到的是无参构造的constructor对象
Constructor constructor=cls.getDeclaredConstructor();
//拿到的是一个参数的constructor对象
Constructor constructor1=cls.getDeclaredConstructor(String.class);
System.out.println(constructor);
System.out.println(constructor1);
//拿三个参数的有参构造
Constructor constructor2=cls.getDeclaredConstructor(String.class,int.class,String.class);
3)当拿到特定的Constructor对象时,就可以通过该对象来实例化Student对象
constructor.setAccessible(true);//破坏封装,仅限当前JVM进程中的这个constructor可用
Student stu= (Student) constructor.newInstance();
constructor.setAccessible(true);//破坏封装,仅限当前JVM进程中的这个constructor可用 Student stu= (Student) constructor.newInstance();
理解:constructor这个对象是描述无参构造的,可以这么理解,一个无参构造在JVM中也可以把它看作是一个类的对象(Constructor类的对象),Constructor这个类就是用来描述任意一个类的构造方法的,此处的constructor就拿到了Student类的无参构造,然后调用setAccessible就相当于在JVM内部通过constructor这个对象把private权限去掉了,然后通过反射来创建了Student类的实例【通过constructor对象来创建了Student类的实例】。
问题:什么叫仅限当前JVM进程中的这个constructor可用?
红框的两行相当于又产生了constructor类的对象constructor1,这个对象也拿到了无参构造,但这个无参构造对constructor1来说仍然是私有的,因为没有破坏封装,因此通过constructor1来创建对象是会报错的,无法创建。要想创建,继续破坏封装。
破坏封装指的是当前JVM进程中的当前反射对象破坏封装,对于其他反射对象而言仍旧是未被破坏的。
完整写法:
Classcls=Student.class; //拿到的是无参构造的constructor对象 Constructor constructor=cls.getDeclaredConstructor(); //反射破坏封装,仅限当前JVM进程中的这个constructor可用 constructor.setAccessible(true); Student stu= (Student) constructor.newInstance(); //有一个参数 Constructor constructor1=cls.getDeclaredConstructor(String.class); //反射破坏封住【正向操作就相当于 Student student1=new Student("张三")】 constructor1.setAccessible(true); Student student1= (Student) constructor1.newInstance("哈哈"); System.out.println(student1); //有三个参数 Constructor constructor2=cls.getDeclaredConstructor(String.class,int.class,String.class); constructor2.setAccessible(true); Student student2= (Student) constructor2.newInstance("哈哈",18,"中国"); System.out.println(student2);
3.Method:与类中方法相关的类
在上面代码的Person类中加如下方法:
private void test(){
System.out.println("Person的test方法,private权限");
}
public void testPerson(){
System.out.println("Person的testPerson方法,private权限");
}
在上面代码的Student类中加如下方法:
private void fun(){
System.out.println("Student类的fun方法,private权限");
}
public int getAge(int num){
System.out.println("Student类的getAge方法,public权限");
return this.age+num;
}
创建MethodTest测试类 :
1)getMethods和getDeclaredMethods
package java_reflect.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
public class MethodTest {
public static void main(String[] args) throws Exception{
//1.要使用反射先拿到该类的class对象
Class> cls=Class.forName("java_reflect.Student");
//2.获取该类的所有方法的Method对象
Method[] methods=cls.getMethods();
System.out.println(Arrays.toString(methods));
}
}
结果:
[public java.lang.String java_reflect.Student.toString(), public int java_reflect.Student.getAge(int), public void java_reflect.Person.testPerson(), public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
分析:
通过cls.getMethods();能拿到当前类以及其父类的所有的public方法!!!
public static void main(String[] args) throws Exception{
//1.要使用反射先拿到该类的class对象
Class> cls=Class.forName("java_reflect.Student");
//2.获取该类的所有方法的Method对象
Method[] methods=cls.getDeclaredMethods();
System.out.println(Arrays.toString(methods));
}
结果:
[public java.lang.String java_reflect.Student.toString(), public int java_reflect.Student.getAge(int), private void java_reflect.Student.fun()]
分析:
通过cls.getDeclaredMethods();仅能拿到当前类的所有权限的方法!!!
提问:那么如果此时想调用Person类的private方法,该如何拿到?
先拿到父类(Person)的class对象【通过getDeclared拿到private方法】,再想后面操作...
2)拿到Method对象后,通过反射来调用对象的方法
给Student类加新方法:
public class Student extends Person{
private String name;
public int age;
String country;
public Student(){//注意此时权限是放开的
System.out.println("Non Parameter");
}
Student(String name) {
System.out.println("One Parameter");
this.name = name;
}
public Student(String name, int age, String country) {
System.out.println("three Parameters");
this.name = name;
this.age = age;
this.country = country;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", country='" + country + ''' +
'}';
}
private void fun(){
System.out.println("Student类的fun方法,private权限");
}
public int getAge(int num){
System.out.println("Student类的getAge方法,public权限");
return this.age+num;
}
private static void staticFunc(String test){
System.out.println(test);
System.out.println("Student类的静态方法");
}
}
调用成员方法:
package java_reflect.Test;
import java_reflect.Student;
import java.lang.reflect.Method;
import java.util.Arrays;
public class MethodTest {
public static void main(String[] args) throws Exception{
//1.要使用反射先拿到该类的class对象
Class> cls=Class.forName("java_reflect.Student");
//2.获取该类的所有方法的Method对象
//拿到private的fun方法
//cls.getDeclaredMethod(方法名称,参数类型)
Method methodFun=cls.getDeclaredMethod("fun");
methodFun.setAccessible(true);
//fun方法是成员方法,必须通过Student类的对象调用
Student stu= (Student) cls.newInstance();
//开始调用
// methodFun.invoke(通过哪个具体对象来调用,方法的参数)
methodFun.invoke(stu);//invoke就是调用的意思
}
}
调用静态方法:
//拿到staticFunc对象
Method staticFunc=cls.getDeclaredMethod("staticFunc", String.class);
staticFunc.setAccessible(true);//若是public权限,就没有破坏封装这一步了
//调用staticFunc方法
//静态方法不需要对象就能调用
staticFunc.invoke(null,"哈哈");
3)总结:通过反射调用一个类的方法的步骤
1.先获取该类的class对象
2.通过getDeclaredMethod或getMethod获取该方法的method反射对象
2.若该方法是一个成员方法,需要产生该类的实例
4.调用method.invoke(该类的实例(静态方法就是null),具体方法参数);
4.Field:描述一个类的属性的反射操作类
1)getFields和getDeclaredFields
package java_reflect.Test;
import java_reflect.Student;
import java.lang.reflect.Field;
import java.util.Arrays;
public class FieldTest {
public static void main(String[] args) {
//1.拿反射对象class
Class> cls=new Student().getClass();
Field[] fields=cls.getFields();
Field[] fields1= cls.getDeclaredFields();
System.out.println(Arrays.toString(fields));
System.out.println("------------------------");
System.out.println(Arrays.toString(fields1));
}
}
package java_reflect.Test;
import java_reflect.Student;
import java.lang.reflect.Field;
import java.util.Arrays;
public class FieldTest {
public static void main(String[] args) {
//1.拿反射对象class
Class> cls=new Student().getClass();
Field[] fields=cls.getFields();
Field[] fields1= cls.getDeclaredFields();
System.out.println(Arrays.toString(fields));
System.out.println("------------------------");
System.out.println(Arrays.toString(fields1));
}
}
结果:
分析:
getFields:拿到当前类和父类的所有public权限的属性
getDeclaredFields:拿到当前类的所有权限(包括private,包访问权限)的属性
3)通过反射来调用对象的属性
package java_reflect.Test;
import java_reflect.Student;
import java.lang.reflect.Field;
public class FieldTest {
public static void main(String[] args) throws Exception {
//1.拿反射对象class
Class> cls=new Student().getClass();
//2.拿到当前的name属性(Student类的私有属性)
Field field=cls.getDeclaredField("name");
field.setAccessible(true);//破坏封装
//3.name是成员属性,有对象才能调用,需要产生Student类的对象
Student student=(Student) cls.newInstance();
//4.调用
//你是通过哪个具体的Student对象来获取属性的
System.out.println(field.get(student));
}
}
结果:name是默认值null
注:给name设置值
//field.set(给哪个具体的对象设置值,设置的具体值) field.set(student,"美女");
3)总结:通过反射调用一个类的属性的步骤(Field的使用)
1.先获取class对象
2.获取指定名称的Field对象
3.创建该类实例(调用成员属性时)
4.set(obj-具体操作的对象,要设置的属性值),给属性修改值或赋值
5.get(obj-具体操作的对象),获取相应的值
三、Lambda表达式
1.概念
1.Lambda表达式(有的语言称为闭包)是JDK1.8引用的重要特性,也是一种编程范式-函数式编程,面向对象的变成有个最大的问题,就是什么都得有个类,有个对象,通过对象去处理数据和方法。
2.在大数据运算中,通常我们只需要关心特定的方法调用即可。
3.在Java中Lambda表达式的前身就是匿名内部类,它就是匿名内部类的简化写法
2.匿名内部与Lambda表达式简化接口实现子类过程
a.接口实现子类正常写法:
interface IMessage{
void printMessage(String msg);
}
class MessageImpl implements IMessage{
@Override
public void printMessage(String msg) {
System.out.println("正经写法,接口子类覆写抽象方法");
System.out.println(msg);
}
}
public class LambdaTest {
public static void main(String[] args) {
IMessage message=new MessageImpl();
message.printMessage("正经用法");
}
}
b.使用匿名内部类来简化子类的实现(假设此时具体的实现只会使用一次,我们不需要每次创建一个类的结构)
interface IMessage{
void printMessage(String msg);
}
//class MessageImpl implements IMessage{
//
// @Override
// public void printMessage(String msg) {
// System.out.println("正经写法,接口子类覆写抽象方法");
// System.out.println(msg);
// }
//}
public class LambdaTest {
public static void main(String[] args) {
// IMessage message=new MessageImpl();
// message.printMessage("正经用法");
//匿名内部类
IMessage message1=new IMessage() {
@Override
public void printMessage(String msg) {
System.out.println(msg);
}
};
message1.printMessage("匿名内部类的用法");
}
}
c.Lambda表达式形式
interface IMessage{
void printMessage(String msg);
}
public class LambdaTest {
public static void main(String[] args) {
IMessage message2=msg -> System.out.println(msg);
message2.printMessage("Lambda表达式的写法");
}
}
Lambda表达式直接省略了所有和类相关的代码(new Imessage全没了),只保留了最核心的方法的代码,包括方法的参数,方法的方法体。
1)任何能使用Lambda表达式的前提:接口只有一个抽象方法
Lambda表达式一般都是结合接口来使用,对于接口有一个要求,就是该接口必须只能有一个抽象方法。(Lambda表达式把方法名称都省了,因此默认就覆写那个抽象方法)
2)@FunctionalInterface注解
此注解用在接口声明上,检查当前的接口是否只包含一个抽象方法
在JDK1.8中对接口做了扩展,允许接口存在普通方法,普通方法使用default关键字声明,包含方法体,普通方法无需覆写。
@FunctionalInterface
interface IMessage{
void printMessage(String msg);
//这是一个普通方法,不是抽象方法,子类不一定需要覆写
default void test() {
System.out.println("普通方法");
}
}
class MessageImpl implements IMessage{
@Override
public void printMessage(String msg) {
System.out.println("正经写法,接口子类覆写抽象方法");
System.out.println(msg);
}
}
public class LambdaTest {
public static void main(String[] args) {
IMessage message=new MessageImpl();
message.test();
}
}
3.Lambda表达式的使用
1)Lambda表达式语法
(参数列表)->{
方法体代码
[return 返回值];
}
2)对于接口的抽象方法分为以下几种情况:(不同情况Lambda表达式的使用)
(参数列表)->{
方法体代码
[return 返回值];
}
2)对于接口的抽象方法分为以下几种情况:(不同情况Lambda表达式的使用)
1.无返回值无参数
a:两个括号表示一个东西,是方法参数,此时无参
b.->表示是方法的实现
c.覆写后的方法代码(方法体)。并且test1()就调用的是它
注意:如果方法体只有一行,大括号可以省略
public class LambdaUsage {
public static void main(String[] args) {
NonReturnNonParameter i1=()->{
//若有多行代码,{}不能省略
System.out.println("第一行代码");
System.out.println("第二行代码");
};
i1.test1();
}
}
interface NonReturnNonParameter{//1.无返回值无参
void test1();
}
2.无返回值有参数
public class LambdaUsage {
public static void main(String[] args) {
NonReturnHasParameter i2=(int age,String name)->{
age=age+1;
name="哈哈";
System.out.println(age+" "+name);
};
i2.test2(18,"");
}
}
interface NonReturnHasParameter{//2.无返回值有参数
void test2(int age,String name);
}
此处参数类型可以省略,若省略了类型,则每个参数类型都要省略。即->前的括号中的int,String都可以不写,甚至参数名称age,name都可以直接用t1,t2,默认就是t1是age,t2是name。但是不可以说int省略了String不省略,这样会报错。
public static void main(String[] args) {
NonReturnHasParameter i2=(t1,t2)->{
t1=t1+1;
t2="哈哈";
System.out.println(t1+" "+t2);
};
i2.test2(18,"");
}
3.有返回值无参数
public class LambdaUsage {
public static void main(String[] args) {
HasReturnNonParameter i3=()->{
int num=0;
//num要经过一大堆运算
num+=10;
return num;
};
int ret=i3.test3();
System.out.println(ret);
}
}
interface HasReturnNonParameter{//3.有返回值无参数
int test3();
}
带返回值的方法只有一行代码,可以省略return关键字和大括号。
4.有返回值有参数
public class LambdaUsage {
public static void main(String[] args) {
HasReturnHasParameter i4=(t1,t2)->{
t1+=100;
System.out.println(t2);
return t1;
};
int ret=i4.test4(18,"铭哥");
System.out.println(ret);
}
}
interface HasReturnHasParameter{//4.有返回值有参数
int test4(int age,String name);
}
4.Lambda表达式作用
Lambda就是一种简化的写法,简化了很多类的代码片段。Lambda表达式是一门学科,大量在大数据运算中使用。



