目录
1、反射的使用
(1)定义
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任 意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信 息以及动态调用对象方法的功能称为java语言的反射机制。
(2)用途
1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应 用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论 是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类 的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。
(3)反射相关的类
(4)常用获得类相关的方法
第一种,使用 Class.forName("类的全路径名"); 静态方法。
前提:已明确类的全路径名。
第二种,使用 .class 方法。
说明:仅适合在编译前就已经明确要操作的 Class
第三种,使用类对象的 getClass() 方法
常用的是forName方法。
一个类对应一个class对象
(5)反射的一些使用实例:
自已定义一个student类,里面的私有变量和公共变量,私有构造方法和共有构造方法。
class Student {
private String name = "bit";
//公有属性age
public 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);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
里面使用反射的方式就可以不new一个对象就创建一个新的对象
使用代码如下:
public static void reflectNewInstance() {
try {
Class<?> c1 = Class.forName("demo.Student");
Student student = (Student) c1.newInstance();
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
此时在主函数中调用reflectNewInstance函数就可以输出student,从代码中可以看出,这里并没有new一个student,却用了Student类中的无参数构造方法来创建出一个对象。
这里也可以在函数中调用public修饰的方法。
并且也可以反射私有的构造方法
代码如下:
public static void reflectPrivateConstructor() {
try {
Class<?> c1 = Class.forName("demo.Student");
Constructor<?> constructor = c1.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
Student student = (Student) constructor.newInstance("YT",19);
System.out.println(student);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
使用getDeclaredConstructor就可以获取到私有的构造方法。
获取和修改私有字段代码:
public static void reflectPrivateField() {
try {
Class<?> c1 = Class.forName("demo.Student");
Field field = c1.getDeclaredField("name");
field.setAccessible(true);
Student student = (Student) c1.newInstance();
field.set(student,"YT");
System.out.println(student);
String str = (String) field.get(student);
System.out.println(str);
} catch (ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
调用私有方法:
public static void reflectPrivateMethod() {
try {
Class<?> c1 = Class.forName("demo.Student");
Method method = c1.getDeclaredMethod("eat");
Student student = (Student) c1.newInstance();
method.setAccessible(true);
method.invoke(student);
method = c1.getDeclaredMethod("function", String.class);
method.setAccessible(true);
method.invoke(student,"私有方法");
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
如果私有方法是有返回值的,就要用和返回值类型相同的变量来接受,但这里记得要类型转换。
所以反射是有利有弊的。
反射优点:
1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
反射缺点:
1. 使用反射会有效率问题。会导致程序效率降低。
2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。
反射总结:反射实际上就是一种机制,在运行期间,在运行期间,可以动态获取当前对象的属性,类型,方法等等(类的信息)
2、枚举的使用
(1)背景和定义
枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来。
可以知道在java里面枚举是一个对象。
public enum TestEnum {
RED,BLACK,GREEN
}
优点:将常量组织起来统一进行管理
应用场景::错误状态码,消息类型,颜色的划分,状态机等等....
public static void main(String[] args) {
TestEnum testEnum = TestEnum.BLACK;
switch (testEnum) {
case RED:
System.out.println("RED");
break;
case BLACK:
System.out.println("BLACK");
break;
case GREEN:
System.out.println("GREEN");
break;
case WHITE:
System.out.println("WHITE");
break;
default:
System.out.println("无对应选择");
break;
}
}
(2)常用方法
public static void main(String[] args) {
TestEnum[] testEnums = TestEnum.values();
for (int i = 0; i < testEnums.length; i++) {
System.out.println(testEnums[i]);
}
}
public static void main(String[] args) {
TestEnum testEnum = TestEnum.valueOf("RED");
System.out.println(testEnum);
}
public static void main(String[] args) {
System.out.println(RED.compareTo(BLACK));
}
用compareTo就可以对枚举定义的顺序进行比较
枚举的构造方法:
public enum TestEnum {
RED("红色",1),BLACK("黑色",2),GREEN("绿色",3),WHITE("白色",4);
public String color;
public int ordinal;
TestEnum(String color,int ordinal) {
this.color = color;
this.ordinal = ordinal;
}
}
重要:枚举的构造方法默认是private的。
(3)枚举的优缺点
优点:
a. 枚举常量更简单安全 。
b. 枚举具有内置方法 ,代码更优雅
缺点:
a. 不可继承,无法扩展
(4)反射的特点
反射不可以获取枚举对象,所以枚举可以用来实现单例模式。(只有一个对象)
3、lambda表达式
(1)背景
Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达 式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。
lambda实则就是一个匿名函数
正常的函数是有:返回值 方法名(参数列表) { 方法体 }、
而匿名函数是没有方法名的,并可以不写返回值。
(2)lambda表达式的语法
基本语法:
(parameters) -> expression 或 (parameters) ->{ statements; }
(3)lambda表达式的语法
函数式接口:
@FunctionalInterface
interface NoParameterNoReturn {
//只能有一个方法
void test();
}
当创建一个继承了这个接口的类,并对这个函数进行重写:
用正常的方式:
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
public class TestDemo {
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("haha");
}
};
}
}
用lambda表达式:
无返回值无参数的lambda表达式:
public class TestDemo {
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = () ->{
System.out.println("haha");
};
noParameterNoReturn.test();
}
}
有返回值有一个参数:
@FunctionalInterface
interface OneParameterHaveReturn {
int test(int a);
}
public class TestDemo {
public static void main(String[] args) {
OneParameterHaveReturn oneParameterHaveReturn = (int a) -> {return a;};
System.out.println(oneParameterHaveReturn.test(10));
}
}
无返回值多个参数:
@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(a + " " + b);
};
moreParameterNoReturn.test(10,11);
}
}
lambda表达式可以简化代码,但是简化代码就会带来代码的可读性降低的问题,并且lambda还可以有更简单的减法。
例如,有返回值有一个参数可以更简化为:
@FunctionalInterface
interface OneParameterHaveReturn {
int test(int a);
}
public class TestDemo {
public static void main(String[] args) {
OneParameterHaveReturn oneParameterHaveReturn = (a) -> a;
System.out.println(oneParameterHaveReturn.test(10));
}
}
所以一般来讲lambda的使用场景是比较少的。
lambda的使用:
例如在构造大根堆和小根堆的时候:
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((o1,o2) -> o1 - o2);
priorityQueue.add(1);
priorityQueue.add(2);
priorityQueue.add(4);
System.out.println(priorityQueue);
(4)变量捕获
class Test {
public void func() {
System.out.println("func()");
}
}
public class TestDemo {
public static void main(String[] args) {
int a = 10;
new Test() {
@Override
public void func() {
System.out.println("这是重写内部类方法");
System.out.println(a);
}
}.func();
}
}
这里的a就是所捕获的变量,这里的a就不能在重写的func里面被修改了。
所以a一定是常量或者是一个没有改变的量。
lambda变量捕获:
同理,在lambda表达式里面,也不能补修改捕获变量:
(5)lambda表达式在集合当中的使用
为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接。
a.foreach在list中的使用:
public class TestDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(123);
list.add(234);
list.add(345);
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
System.out.println("==============");
list.forEach((a) -> System.out.println(a));
}
}
b.foreach在hashmap中的使用
public class TestDemo {
public static void main(String[] args) {
HashMap<Integer,String> hashMap = new HashMap<>();
hashMap.put(1,"123");
hashMap.put(2,"234");
hashMap.put(3,"345");
hashMap.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer integer, String s) {
System.out.println("key:"+integer + " value:"+s);
}
});
System.out.println("=================");
hashMap.forEach((Integer integer,String s) -> System.out.println("key:"+integer + " value"+s));
}
}
结果:
使用lambda表达式就可以用一行代码解决多行代码才能解决的问题,但是这也明显地降低了代码的可读性,所以一般较少使用。
(5)lambda表达式的总结
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:
1. 代码简洁,开发迅速
2. 方便函数式编程
3. 非常容易进行并行计算
4. Java 引入 Lambda,改善了集合操作
缺点:
1. 代码可读性变差
2. 在非并行计算中,很多计算未必有传统的 for 性能要高
3. 不容易进行调试