目录
一.反射
1.概念:在运⾏时检查、访问和修改类、接⼝、字段和⽅法的机制
2.Class类
3.反射相关的类型
4.各类型对应的方法
编辑 5.代码示例
(1).class类方法
(2).Field类方法
(3).Constructor类方法
(4).Method类方法
6.总结
二.枚举
1.概念:主要⽤途是将⼀组常量组织起来,在这之前表⽰⼀组常量通常使⽤定义常量的⽅式
2.枚举用法
<1>.switch语句
<2>.枚举方法
3.枚举的特殊
4.应用
5.总结
三.lambda表达式
1.语法
2.函数式接口
3.lambda表达式例子
<1>无返回无参数的函数式接口
编辑 <2>⽆返回值⼀个参数
<3>⽆返回值多个参数
<4>.有返回值⽆参数
<5>有返回值⼀个参数
<6>有返回值多参数
<7>精简总结
4.变量捕获
5.应用
<1>Collection接⼝
<2>List接⼝
<3> Map
6.总结
一.反射
1.概念:在运⾏时检查、访问和修改类、接⼝、字段和⽅法的机制
通俗来讲就是在程序运行时,可以通过反射拿到类中各种属性 字段,甚至还可以直接创建类的对象并且修改对应的属性
eg:你拿到一个不知道是什么类的对象,但通过反射,你可以去查看它是哪个类的,这个类有什么方法可以用,然后还能调用这些方法
2.Class类
Java⽂件被编译后,⽣成了.class⽂件,JVM此时就要去解读.class⽂件 ,被编译后每个.Java⽂件.最终变成了Class类对象的⼀个实例。
3.反射相关的类型

4.各类型对应的方法
5.代码示例
假设有以下类
package reflection;class Student {//私有属性nameprivate String name = "bit";//公有属性agepublic int age = 18;//不带参数的构造⽅法public Student(){System.out.println("Student()");}private Student(String name,int age) {this.name = name;this.age = age;System.out.println("Student(String,name)");}private void eat(){System.out.println("i am eat");}public void sleep(){System.out.println("i am pig");}private void function(String str) {System.out.println(str);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
(1).class类方法
<1>.获得class对象
public static void main(String[] args) {// 1.Class.forName("路径名")//注意 :要是在src目录底下,forName 里直接写类名// 在包下面则是 包名 + 类名Class<?> c1;try {c1 = Class.forName("reflection.Student");//这种写法最常用但要处理受查异常} catch (ClassNotFoundException e) {throw new RuntimeException(e);}// 2. 类名 + classClass<?> c2 = Student.class;// 3.getClass方法// 但使用要创建Student对象Student student = new Student();Class<?> c3 = student.getClass();//这里虽然获得三次class对象,但获得的都是一个 class对象//因为 class在jvm中,创建的所有对象都是基于class这个模板创建的//获得的都是这个模板System.out.println(c1.equals(c2));//结果:trueSystem.out.println(c1.equals(c3));//结果:trueSystem.out.println(c2.equals(c3));//结果:true}
<2>.其他方法介绍
public static void main(String[] args) {try {// 1.获得类的加载器Class<?> c1 = Class.forName("reflection.Student");ClassLoader classLoader = c1.getClassLoader();System.out.println(classLoader);//运行结果:jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b// 2.查看该类中所有的类和接口类Class<?>[] declaredClasses = c1.getDeclaredClasses();System.out.println(declaredClasses);//结果:[Ljava.lang.Class;@3b07d329// 3.根据类名获得对象(前面有)// 4.创建类的实例(这里必须获得class类对象)Student o = (Student) c1.newInstance();System.out.println(o);//结果://有参在下面 constructor 中讲到// Student()--->调用的是无参构造方法// Student{name='bit', age=18}// 5.获得类的完整路径名字String name = c1.getName();System.out.println(name);//结果:reflection.Student} catch (ClassNotFoundException e) {throw new RuntimeException(e);}catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}
(2).Field类方法
public static void main(String[] args) {//剩下的这些 是基于 class类 实现的try {//获得class对象Class<?> c1 = Class.forName("reflection.Student");//实例对象Student student = (Student)c1.newInstance();// 1.获得某个公有的属性对象Field age = c1.getField("age");System.out.println(age);//结果直接将该类对应的具体信息打印出来//结果:public int reflection.Student.age//若想打印该属性对象的值,则以下做法Object o = age.get(student);//获得student对象 里的 age属性对象的值System.out.println(o);//结果:18// 2.获得所个公有的属性对象Field[] fields = c1.getFields();//以数组形式返回for (Field f : fields) {System.out.println(f);}//结果;public int reflection.Student.age// 3.获得某个属性对象Field name = c1.getDeclaredField("name");//注意:不能直接打印// 因为获得的 name 是 Private 修饰的// 不允许 类外 直接获取// 而反射获取要加 setAccessible 确认获取name.setAccessible(true);System.out.println(name);//结果:private java.lang.String reflection.Student.nameField[] declaredFields = c1.getDeclaredFields();for (Field f : fields) {System.out.println(f);}//结果:// private java.lang.String reflection.Student.name// public int reflection.Student.age// 5.修改属性的值System.out.println("==================");Field name1 = c1.getDeclaredField("name");name1.setAccessible(true);name1.set(student,"王大锤");//修改student里面的name值为“王大锤”Object string = name1.get(student);System.out.println(string);//结果:王大锤 } catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (NoSuchFieldException e) {throw new RuntimeException(e);}}
(3).Constructor类方法
public static void main(String[] args) {try {//获得class对象Class<?> c1 = Class.forName("reflection.Student");// 1.获得该类中与参数类型匹配的公有构造方法//获得无参构造方法Constructor<?> constructor = c1.getConstructor();Student student = (Student)constructor.newInstance();System.out.println(student);//结果:// Student()// Student{name='bit', age=18}Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class,int.class);//详解看下图declaredConstructor.setAccessible(true);Student student1 = (Student)declaredConstructor.newInstance("王大锤", 666);System.out.println(student1);// 结果:// Student(String,name)// Student{name='王大锤', age=666}// 2.获得所有的构造方法Constructor<?>[] declaredConstructors = c1.getDeclaredConstructors();for (Constructor<?> d :declaredConstructors) {System.out.println(d);}//结果:// private reflection.Student(java.lang.String,int)// public reflection.Student()//另外一个获得公有构造方法同理,这里就不演示了} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}
(4).Method类方法
public static void main(String[] args) {try {Class<?> c1 = Class.forName("reflection.Student");// 1.获得该类某个公有和私有的方法Method sleep = c1.getMethod("sleep");System.out.println("获得的公有方法名>:"+sleep.getName());//结果: 获得的公有方法名>:sleepMethod function = c1.getDeclaredMethod("function", String.class);function.setAccessible(true);System.out.println("获得的私有方法名>:"+sleep.getName());// 结果: 获得的私有方法名>:sleep// 2.获得该类所有方法(另一个类似,这里就不展示了)Method[] declaredMethods = c1.getDeclaredMethods();for(Method m : declaredMethods){System.out.print(m.getName()+" ");}//结果:获得的私有方法名>:sleep} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}
6.总结
优点:
1. 可以获得一个类的各种属性 方法,以及根据 对象调用其他的方法
2. 增加程序的灵活性和扩展性,降低耦合性
缺点:
1. 使⽤反射会有效率问题,会导致程序效率降低(调用的方法太多了,栈帧开销大,效率低)
2. 难以维护
3.代码量大(eg: 正常实例化就可以调用属性方法了,而反射不但要处理异常 而且要得到class对象再完成实例化,万一要调用其它有参构造方法还得获得 等等 )
二.枚举
1.概念:主要⽤途是将⼀组常量组织起来,在这之前表⽰⼀组常量通常使⽤定义常量的⽅式
eg:定义三个常量
public static final int RED = 1 ;public static final int GREEN = 2 ;public static final int BLACK = 3 ;
public enum TestEnum {RED,BLACK,GREEN;}
2.枚举用法
public enum MyEnum {//创建一个枚举类把常量存储RED,GREEN,BLACK;}
<1>.switch语句
public enum MyEnum {//创建一个枚举类把常量存储RED,GREEN,BLACK;public static void main(String[] args) {//拿到枚举对象MyEnum myEnum = MyEnum.RED;switch(myEnum){case RED :System.out.println(RED);break;case GREEN:System.out.println(GREEN);//运行结果:REDbreak;case BLACK:System.out.println(BLACK);break;default:break;}}
}
<2>.枚举方法
// 1.以数组形式返回枚举类型里所有成员// 同时获取枚举成员的索引位置MyEnum[] values = MyEnum.values();for(int i = 0; i < values.length;i++){System.out.println("枚举对象>:"+values[i]+" 索引位置>:"+values[i].ordinal());}//运行结果:// 枚举对象RED 索引位置0// 枚举对象GREEN 索引位置1// 枚举对象BLACK 索引位置2
// 2.将字符串装换为枚举实例MyEnum myEnum = MyEnum.valueOf("RED1");System.out.println(myEnum);//结果:RED
若输入的字符串不在枚举对象中,则会创建失败,会报错
// 3.比较两个枚举成员在定义时的顺序//其实是按照下标来计算的int result = RED.compareTo(BLACK);System.out.println(result);//结果: -2
到这里就有问题了,当前枚举类没有实现Comparable接口,那这个compareTo方法哪里来的?
3.枚举的特殊
1. 所有的自定义枚举类都默认继承Enum类,而Enum类实现了Comparable接口并重写了compareTo方法,所以自定义枚举类访问的是父类的compareTo方法,也就是说子类这四种方法访问的是Enum父类的方法
2. 枚举的构造⽅法默认是私有的
4.与反射应用
最终代码如下
public static void main(String[] args) {try {//拿到class对象Class<?> c1 = Class.forName("MyEnum.MyEnum");//拿到 私有构造方法Constructor<?> constructor =c1.getDeclaredConstructor(String.class,int.class,String.class, int.class);constructor.setAccessible(true);//实例化对象MyEnum myEnum =(MyEnum) constructor.newInstance("66","1","周六", 60);//父类随便传参System.out.println(myEnum);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}
运行一下,如何
4.应用
单例模式,图书管理器的那块单例模式还有一个小bug,那就是我可以通过反射拿到私有构造方法创建一堆对象,而枚举避免了这一点
//引入枚举的单例模式
public enum TestEnum {//拿到枚举对象//这里记住:枚举的意义是将常量存储在一起// 所以枚举对象是 Ststic 修饰,全局都是一份INSTANCE;public static TestEnum getInstance(){return INSTANCE;}
}
class test1{public static void main(String[] args) {TestEnum testEnum = TestEnum.getInstance();TestEnum testEnum1 = TestEnum.getInstance();//所以这里拿到都是同一份System.out.println("两个实例是否相同:"+(testEnum.equals(testEnum1)));//结果:// 两个实例是否相同:true}
}
5.总结
枚举
优点:1. 枚举常量更简单安全 。
2. 枚举具有内置⽅法 ,代码更优雅3.枚举默认继承Enum类,且构造方法只允许private缺点:延展性差一道阿里巴巴2017年面试题:为什么枚举实现单例模式 是安全的?想必大家都知道了吧
三.lambda表达式
1.语法
(parameters) -> expression 或 : (parameters) -> {expression;}1. . paramaters:类似⽅法中的形参列表,这⾥的参数是函数式接⼝⾥的参数。2.. ->:可理解为“被⽤于”的意思3.. ⽅法体:可以是表达式也可以代码块,是函数式接⼝⾥⽅法的实现。
2.函数式接口
函数式接口有且只能有一个抽象类方法
还可以写defalut修饰的方法,这也是函数式接口
@FunctionalInterface
interface NoParameterNoReturn {void test();default void test2() {System.out.println("JDK1.8新特性,default默认⽅法可以有具体的实现");}
}
3.lambda表达式例子
lambda表达式是内部类的一种简介写法
<1>无返回无参数的函数式接口
此时运行结果会打印 --这是无参无返回值的方法 --,那么用lambda表达式的结果为
<2>⽆返回值⼀个参数
//⽆返回值⼀个参数
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}
public class TestDemo {public static void main(String[] args) {//写成内部类OneParameterNoReturn oneParameterNoReturn = new OneParameterNoReturn() {@Overridepublic void test(int a) {System.out.println("这是⽆返回值⼀个参数的方法");}};//引入lambda表达式(常规写法)OneParameterNoReturn oneParameterNoReturn1 = (int a)-> System.out.println("这是⽆返回值⼀个参数的方法");//省略 参数类型OneParameterNoReturn oneParameterNoReturn2 = (a)-> System.out.println("这是⽆返回值⼀个参数的方法");// 因为参数列表只有一个参数,所以可以去掉括号()OneParameterNoReturn oneParameterNoReturn3 = a-> System.out.println("这是⽆返回值⼀个参数的方法");}
<3>⽆返回值多个参数
//⽆返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a,int b);
}public class TestDemo {public static void main(String[] args) {//常规写法MoreParameterNoReturn moreParameterNoReturn = (int a,int b)-> System.out.println("这是⽆返回值多个参数的方法");//可以将 参数列表 阐述类型int 去掉MoreParameterNoReturn moreParameterNoReturn0 = (int a,int b)-> System.out.println("这是⽆返回值多个参数的方法");}
<4>.有返回值⽆参数
//有返回值⽆参数
@FunctionalInterface
interface NoParameterReturn {int test();
}
public class TestDemo {public static void main(String[] args) {//常规写法NoParameterReturn noParameterNoReturn = ()->{return 10;};// 因为方法体有且只有一条语句且是return语句,可以将 { } 与 return 省略NoParameterReturn noParameterNoReturn1 = ()->10;System.out.println(noParameterNoReturn1.test());//结果为: 10}
<5>有返回值⼀个参数
//有返回值⼀个参数
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}
public class TestDemo {public static void main(String[] args) {//常规写法OneParameterReturn oneParameterNoReturn = (int a)->{return a;};//因为方法体有且只有一条语句且是return语句,可以将 { } 与 return 省略OneParameterReturn oneParameterNoReturn1 = (int a)->a;//因为参数列表只有一个参数,可以去掉括号 和 类型OneParameterReturn oneParameterNoReturn2 = a->a;}
<6>有返回值多参数
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {int test(int a,int b);
}public class TestDemo {public static void main(String[] args) {//常规写法MoreParameterReturn moreParameterReturn = (int a,int b)->{return a + b;};//简化后MoreParameterReturn moreParameterReturn1 = ( a,b)->a + b;}
<7>精简总结
1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。2. 参数的⼩括号⾥⾯只有⼀个参数,那么⼩括号可以省略3. 如果⽅法体当中只有⼀句代码,那么⼤括号可以省略4. 如果⽅法体中只有⼀条语句,且是return语句,那么⼤括号可以省略,且去掉return关键字。
4.变量捕获
5.应用
在Java中,也增加了一些使用函数式接口的方法
<1>Collection接⼝
这里介绍常用方法 forEach()
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");//正常打印就是 for循环 foreach循环 迭代器 三者之一打印//这里介绍 forEach 打印//使用内部类(下面图进行介绍)list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});//结果:// Hello// bit// hello// lambda}
写成lambda表达式
list.forEach(S-> System.out.print(S+" "));//Hello bit hello lambda
<2>List接⼝
之前的sort比较大小,可以传递比较器,之前都是类实现接口再传递对象,这里用内部类与lambda表达式
目前学的三种
引入lambda表达式
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");list.sort((o1,o2)->o1.length() - o2.length());System.out.println(list);}
结果//[bit, Hello, hello, lambda]
<3> Map
内部类操作
public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach(new BiConsumer<Integer, String>(){@Overridepublic void accept(Integer k, String v){System.out.println(k + "=" + v);}});}
解释
引入lambda表达式
public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach((k,v)-> System.out.println(k+"="+v));}
结果//1=hello//2=bit//3=hello//4=lambda
6.总结
有点:代码简洁,⽅便函数式编程(就是函数式接口)
缺点:1.不好读,要看源码并结合lambda表达式看
2.在⾮并⾏计算中,很多计算未必有传统的 for 性能要⾼(再javaEE会讲到,这里了解即可)
欧克~~到这里这些完结了,如果对你有帮助请点个赞吧(!!!!球球了)
拜拜!!