Java 学习日记(4)

反射

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能够直接操作任何对象的内部属性和方法。

加载完类之后,在堆内存的方法区种就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就相当于一面镜子,透过这个镜子看到类的结构,我们称之为反射。

正常方式: 引入需要的"包类"名称 -> 通过new实例化 -> 取得实例化对象
反射方式: 实例化对象->getClass()方法->得到完整的"包类"名称

获取Class类实例的几种方式

package com.an.java;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author an
 * @create 2021-04-15-19:41
 */
public class ReflectionTest {
	/**
	 * 反射之前的操作
	 */
	@Test
	public void test1() {
		//1、创建Person类的对象
		Person p1 = new Person("Tom",12);

		//2、通过对象调用其内部的属性和方法
		p1.age = 10;
		System.out.println(p1.toString());

		p1.show();

		// 在Person类的外部,是不可以通过Person类的对象调取其私有的结构
		// 比如: name,showNation()以及私有的构造器
	}

	/**
	 * 反射之后,对于Person的操作
	 */
	@Test
	public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
		Class c = Person.class;
		// 1、通过反射可以创造Person类的对象
		Constructor cons = c.getConstructor(String.class,int.class);
		Object obj = cons.newInstance("tom",12);
		Person p = (Person)obj;
		System.out.println(p.toString());
		// 2、通过反射调用指定的属性和方法
		Field age = c.getDeclaredField("age");
		age.set(p,10);
		System.out.println(p.toString());

		// 3、调用方法
		Method show = c.getDeclaredMethod("show");
		show.invoke(p);

		// 4、通过反射,可以调用私有结构
		Constructor cons1 = c.getDeclaredConstructor(String.class);
		cons1.setAccessible(true);
		Person p1 = (Person)cons1.newInstance("Jerry");


		// 调用私有的属性
		Field name = c.getDeclaredField("name");
		name.setAccessible(true);
		name.set(p1,"jazz");
		System.out.println(p1);

		// 调用私有方法
		Method nation = c.getDeclaredMethod("showNation", String.class);
		nation.setAccessible(true);
		String na = (String)nation.invoke(p1,"中国");
		System.out.println(na);
	}

	// 通过直接 new 的方式或反射的方式都可以调用公共的结构,开发中如何选择?
	// 建议: 直接new的方式
	// 什么时候使用反射,反射的方式,反射的特征: 动态性


	// 反射机制与面向对象的封装性是否矛盾?
	// 不矛盾, 封装性可以看作一个建议, 反射解决的是能不能的问题


	/**
	 * 关于 java.lang.Class 类的理解
	 * 1、类的加载过程
	 * 程序在经过 javac.exe 命令后,会生成一个或多个字节码文件(.class结尾),
	 * 接着使用java.exe对某个字节码文件进行解释和运行,相当于把字节码文件加载
	 * 到内存中,此过程称为类的加载。加载到内存中的类,称为运行时类,此运行时类
	 * ,就作为Class的一个实例
	 * 2、换句话说,Class 的实例就对应着一个运行时类
	 * 3、加载到内存中的运行时类,会缓存一定时间,在此时间内,我们可以通过
	 * 不同的方式来获取运行时类
	 */
	// 获取Class实例的方式
	@Test
	public void test3() throws ClassNotFoundException {
		// 方式1: 调用运行时类的属性
		Class<Person> class1 = Person.class;
		System.out.println(class1);

		// 方式2: 通过运行时类的对象,调用getClass()方法
		Person p1 = new Person();
		Class class2 = p1.getClass();

		// 方式3: 调用Class的静态方法: forName(String classPath)
		Class class3 = Class.forName("com.an.java.Person");

		System.out.println(class1 == class2);
		System.out.println(class2 == class3);

		// 方式4: 使用类的加载器
		ClassLoader classLoader = ReflectionTest.class.getClassLoader();
		Class class4 = classLoader.loadClass("com.an.java");
	}
	/**
	 * 有Class对象的类型
	 * 1、class: 外部类,成员
	 * 2、interface: 接口
	 * 3、[]: 数组
	 * 4、enum: 枚举
	 * 5、annotation: 注解 @interface
	 * 6、primitive type: 基本数据类型
	 * 7、void
	 */
}

ClassLoader

package com.an.java;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author an
 * @create 2021-04-15-20:50
 */
public class ClassLoaderTest {

	@Test
	public void test1() {
		// 对于自定义类,使用系统类加载器进行加载
		ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
		System.out.println(classLoader);
		// 调用系统类加载器的getParent(): 获取扩展类加载器
		ClassLoader classLoader1 = classLoader.getParent();
		System.out.println(classLoader1);
		// 调用扩展类加载器的getParent(): 无法获取引导类加载器
		// 引导类加载器主要负责加载java的核心类库,无法加载自定义类
		ClassLoader classLoader2 = classLoader1.getParent();
		System.out.println(classLoader2);

		ClassLoader classLoader3 = String.class.getClassLoader();
		System.out.println(classLoader3);
	}

	/**
	 * Properties: 用来读取配置文件
	 */

	@Test
	public void test2() throws IOException {
		Properties pros = new Properties();

		// 默认在modules下
		FileInputStream fis = new FileInputStream("jdbc.properties");
		pros.load(fis);

		String user = pros.getProperty("user");
		String password = pros.getProperty("password");

		System.out.println("user:"+user+" password:"+password);


		// 默认在 src 下
		ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
		InputStream is = classLoader.getResourceAsStream("jdbc.properties");
		pros.load(is);
	}

}

创建运行时类的对象

package com.an.java;

import org.junit.Test;

import java.util.Random;

/**
 * 通过反射创建对应运行时类对象
 *
 * @author an
 * @create 2021-04-16-10:16
 */
public class NewInstanceTest {
	@Test
	public void test1() throws IllegalAccessException, InstantiationException {
		/*
		newInstance(): 调用此方法,创建对应的运行时类的对象
		内部调用了运行时类的空参构造器
		要想此方法正常创建运行时类的对象,要求:
		1、运行时类必须提供空参的构造器
		2、空参的构造器的访问权限够,通常,设置为public

		在 javabean 中要求提供一个 public 的空参构造器,原因:
		1、便于通过反射,创建运行时类的对象
		2、便于子类继承此运行时类,默认调用super()时,有父类构造器
		 */
		Class<Person> c = Person.class;
		Object obj = c.newInstance();


	}


	// 体会反射的动态性
	@Test
	public void test2() {
		int num = new Random().nextInt(3);
		String classPath = "";
		switch (num) {
			case 0:
				classPath = "java.util.Date"; break;
			case 1:
				classPath = "java.lang.Object"; break;
			case 2:
				classPath = "com.an.java.Person"; break;
		}
		Object obj=null;
		try {

			obj = getInstance(classPath);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}
		System.out.println(obj);
	}

	/*
	创建一个指定类的对象

	 */

	public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
		Class c = Class.forName(classPath);
		return  c.newInstance();
	}

}

反射的应用: 动态代理

代理设计模式的原理:

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

之前涉及到的代理为静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发过程中必然产生过多代理。最好可以通过一个代理类完成全部的代理功能。

静态代理举例

package com.an.java;

/**
 * 静态代理举例
 * 代理类和被代理类在编译期间就已经确定下来了
 *
 *
 * @author an
 * @create 2021-04-16-10:39
 */

interface ClothFactory {
	void produceCloth();
}


class ProxyClothFactory implements ClothFactory{

	private ClothFactory factory; // 用被代理类实例化

	public ProxyClothFactory(ClothFactory factory) {
		this.factory = factory;
	}

	@Override
	public void produceCloth() {
		System.out.println("准备工作");
		factory.produceCloth();
		System.out.println("后续工作");
	}


}

// 被代理类

class NikeClothFactory implements ClothFactory {

	@Override
	public void produceCloth() {
		System.out.println("生产Nike");
	}
}

public class StaticProxyTest {
	public static void main(String[] args) {
		// 创建被代理类的对象
		ClothFactory nike = new NikeClothFactory();
		// 创建代理类的对象
		ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

		proxyClothFactory.produceCloth();
	}
}

动态代理举例

package com.an.java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理举例
 * @author an
 * @create 2021-04-16-10:50
 */
interface Human {
	String getBelief();
	void eat(String food);
}

// 被代理类
class SuperMan implements Human{

	@Override
	public String getBelief() {
		return "I believe I can fly";
	}

	@Override
	public void eat(String food) {
		System.out.println("我喜欢吃" + food);
	}
}

/**
 * 要想实现动态代理,需要解决的问题
 * 问题一: 如何根据加载到内存中的被代理类,动态创建一个代理类及对象
 * 问题二:通过代理类的对象调用方法时,如何调用被代理类中对应的方法
 */

class ProxyFactory {
	// 调用此方法,返回一个代理类的对象,解决问题一
	public static Object getProxyInstance(Object obj) {
		MyInvocationHander hander = new MyInvocationHander();
		hander.bind(obj);
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hander);

	}

}

class MyInvocationHander implements InvocationHandler {

	private Object obj; // 赋值时,需要使用被代理对象赋值
	public void bind(Object obj) {
		this.obj = obj;
	}

	// 当我们通过代理类的对象,调用方法a时,就会自动调用如下的方法: invoke()
	// 将被代理类要执行的方法a的功能声明在invoke()中
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// method: 即为代理类调用的方法,此方法也成为被代理类调用方法
		Object returnValue = method.invoke(obj, args);
		// 上述方法的返回值就作为invoke()方法的返回值
		return returnValue;
	}
}

public class ProxyTest {
	public static void main(String[] args) {
		SuperMan superMan = new SuperMan();
		Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
		// 当通过代理类对象调用方法时,会自动调用被代理类中同名的对象
		proxyInstance.getBelief();
		proxyInstance.eat("火锅");
		
		
	}
}

Lambda 表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

package com.an.java;

import org.junit.Test;

import java.util.Comparator;
import java.util.function.Consumer;

/**
 * Lambda 表达式的使用
 *
 * 1、举例: (o1,o2) -> Integer.compare(o1,o2);
 *
 * 2、格式
 *    ->: Lambda操作符 或 箭头操作符
 *    ->: 左边 Lambda 形参列表,其实是接口抽象方法的形参列表
 *    ->: 右边 Lambda 体(重写抽象方法的方法体)
 *
 * 3、重写,分为6种情况
 *
 * 4、Lambda 表达式的本质: 作为函数式接口的实例
 *
 * 5、如果一个接口只声明了一个方法,则这个接口可以称为函数式接口
 *
 * 6、我们可以在一个接口上使用 @FunctionalInterface 注解,这样可以
 * 检查它是否是一个函数式接口。同时javadoc也会包含一个声明
 * 
 *
 * @author an
 * @create 2021-04-16-14:57
 */
public class LambdaTest1 {
	// 语法一: 无参,无返回值
	@Test
	public void test1() {
		Runnable r1 = () -> System.out.println("Helloworld");
		r1.run();
	}


	// 语法二: 需要一个参数,但是没有返回值
	@Test
	public void test2() {
		Consumer<String> con = new Consumer<String>() {

			@Override
			public void accept(String s) {
				System.out.println(s);
			}
		};

		con.accept("c");

		Consumer<String> con1 = (String s) -> {
			System.out.println(s);
		};
		con1.accept("b");
	}

	// 语法三: 数据类型可以省略,可以由编译器推断得出,称为"类型推断"
	@Test
	public void test3() {
		Consumer<String> con1 = (s) -> {
			System.out.println(s);
		};
		con1.accept("b");
	}

	// 语法四: Lambda 表达式只需要一个参数,小括号可以省略
	@Test
	public void test4() {
		Consumer<String> con1 = s -> {
			System.out.println(s);
		};
		con1.accept("b");
	}

	// 语法五: 需要两个或以上的参数,多条执行语句,可以有返回值
	@Test
	public void test5() {
		Comparator<Integer> com2 = (o1, o2) -> {
			return Integer.compare(o1,o2);
		};
		int compare2 = com2.compare(12,21);
		System.out.println(compare2);
	}

	// 语法六: Lambda 体只有一条语句,return 与大括号都可以省略
	@Test
	public void test6() {
		Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1,o2);

		int compare2 = com2.compare(12,21);
		System.out.println(compare2);
	}
}

方法引用与构造器引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。

方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。

格式: 使用操作符"::"将类或对象与方法名分隔开来。

如下三种主要使用情况:

  • 对象::实例方法名

  • 类::静态方法名

  • 类::实例方法名

构造器引用:和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。

String API

Stream API(java.util.stream)把真正的函数式编程风格引入到Java中。

Stream 是 Java8 中处理集合的关键抽象概念,可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。

Stream 和 Collection 集合的区别: Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者主要面向内存,存储在内存中,后者主要面向CPU,通过CPU实现计算。

①Stream 自己不会存储元素

②Stream 不会改变源对象,他们会返回一个有结果的Stream

③Stream 操作是延迟执行的,意味着他们会等到需要结果的时候才执行。

Stream 操作的三个步骤

1、创建 Stream

一个数据源(集合、数组),获取一个流。

2、中间操作

一个中间操作链,对数据源的数据进行处理。

3、终止操作

一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用。

package com.an.java;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * Stream API 实例化
 * @author an
 * @create 2021-04-16-15:33
 */
public class StreamAPITest {
	@Test
	public void test1() {
		/*
		通过集合
		 */
		// default Stream<E> stream(): 返回一个顺序流
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		Stream<Integer> stream = list.stream();

		// default Stream<E> parallelStream(): 返回一个并行流
		Stream<Integer> stream2 = list.parallelStream();

		/*
		通过数组
		 */
		// 调用 Arrays 类的 static <T> Stream<T> stream(T[] array);
		int arr[] = new int[]{1,2,3,4};
		IntStream stream3 = Arrays.stream(arr);

		/*
		通过Stream 类本身 of()
		 */
		Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5);


		/*
		创建无限流
		 */

		// 迭代
		// 遍历前10个偶数
		Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
	
		Stream.generate(Math::random);
	}
}

posted @ 2021-04-15 22:57  ans20xx  阅读(71)  评论(0编辑  收藏  举报