Java基础知识(下)
Java小黄书知识点(下)
第14章:枚举
1. 枚举关键词:enum
// 简单使用
public enum Color {
// 枚举内容
RED,BLUE,GREEN
}
public class dome01 {
public static void main(String[] args) throws Exception {
// 获取需要的枚举元素
System.out.println(Color.RED);
// 也可以使用Color.values()遍历所有枚举元素
for (Color color:Color.values()){
System.out.println(color);
}
}
}
2. 枚举类:Enum
• 可以看成每一个枚举元素就是一个 Enum
的实例化,如 Color
中的 RED
...
public class dome01 {
public static void main(String[] args) throws Exception {
// Color.RED就是Enum类的实例化对象
System.out.println(Color.RED.name());
}
}
3. 为枚举元素设置属性赋值(更详细的枚举)
public enum Color {
// 枚举内容
RED("红色"),BLUE("蓝色"),GREEN("绿色");
private String name;
// 设置构造方法,可以是私有的外部就无法添加枚举元素了
Color(String name) {this.name = name;}
public String getName() {return name;}
}
public class dome01 {
public static void main(String[] args) throws Exception {
// 获取枚举的中文name
System.out.println(Color.RED.getName());
}
}
4. 类集对枚举的支持--EnumMap
/EnumSet
• 就是枚举元素作为类集中的元素
4.1 EnumMap
子类
• Map
的子类,该类中的 key
就是一个枚举元素
public class dome01 {
public static void main(String[] args) throws Exception {
// 需要获得到Color枚举的反射.class
EnumMap<Color, Integer> enumMap = new EnumMap<Color, Integer>(Color.class);
// 枚举元素作为key
enumMap.put(Color.BLUE,0);
enumMap.put(Color.RED,1);
enumMap.put(Color.GREEN,2);
System.out.println(enumMap.get(Color.RED));
}
}
4.2 EnumSet
子类
• 将枚举中的元素作为集合的元素,不能重复
• 不能使用 new
进行实例化,需要 EnumSet
类的指定方法进行
public class dome01 {
public static void main(String[] args){
// 获得到Color枚举的反射.class直接给到EnumSet中
EnumSet<Color> colors = EnumSet.allOf(Color.class);
for (Color color:colors){
System.out.println(color);
}
}
}
5. 枚举实现接口和定义抽象方法
• 枚举类可以实现接口,但是每个枚举元素都要分别实现接口中的方法
• 枚举类中可以定义抽象方法,同样每个枚举元素都要分别实现抽象方法
public enum Color {
// 枚举内容,分别实现抽象方法
RED {public void getName() {} },
BLUE {public void getName() {} },
GREEN {public void getName() {} };
// 定义抽象方法
public abstract void getName();
}
第15章:Java反射机制
• 反射:简单理解为可以通过实例化对象获取到类的完整信息的过程
1. 认识 Class
类
• 相当于所有类的一个框架(其中包含:属性,构造方法也是框架内容,只是在自定义类时进行具体设计)
• Class
类功能:假如得到一个类的完整路径则可以将其实例化,则 Class
类功能则是根据实例化对象获取类的信息
• 我们常见的类(User
,String
...)都是 Class
类的一个实例
• 获取 Class
类实例化的方法
public class dome01 {
public static void main(String[] args) throws ClassNotFoundException {
// 方法一:通过获取类的路径创建Class类的实例化(从根包开始.)
Class<?> userClass1 = Class.forName("io.User");
// 方法二:根据实例化对象.getClass()获取Class类的实例化
User user = new User();
Class<?> userClass2 = user.getClass();
// 方法三:根据类的class属性直接获得Class类的实例化
Class<User> userClass3 = User.class;
}
}
提示:Class
实例化:可以将其看成在 Class
类框架下根据自己的需求填写数据(例如:上面 User
类就是在 Class
的属性,构造方法,方法下填写自己的具体内容)
所以,User
类就是 Class
的实例化对象
根据 Class
类来实例化对象(不通过 new
)
public class dome01 {
public static void main(String[] args) throws Exception {
// 根据Class类来实例化对象(不通过new)
// 获取Class的实例化User
Class<?> userClass = Class.forName("io.User");
// 实例化User
User user =(User) userClass.newInstance();
}
}
2. 获取类的完整结构
• 类的结构主要分为:
Constructor
:表示类中的构造方法Method
:表示类中的方法Field
:表示类中的属性
2.1 Constructor
(构造方法)
• 类中的一个构造方法代表一个 Constructor
的实例化
根据获取有参构造方法实例化 User
public class dome01 {
public static void main(String[] args) throws Exception {
// 获取Class的实例化User
Class<?> userClass = Class.forName("io.User");
// 获取全部的构造方法
Constructor<?>[] constructors = userClass.getConstructors();
// 根据构造方法实例化User,参数为构造方法中需要的参数
User user =(User) constructors[0].newInstance("lis", 18);
System.out.println(user);
}
}
2.2 Method
(方法)
• 类中的一个方法代表 Method
一个实例化
根据获取方法进行使用
public class dome01 {
public static void main(String[] args) throws Exception {
// 获取Class的实例化User
Class<?> userClass = Class.forName("io.User");
// 根据方法名字,方法参数获取到需要的方法,方法参数需要是参数类型的Class对象
Method userMethod = userClass.getMethod("setHello", String.class);
// invoke使用方法,需要使用方法的实例,和方法本来需要的参数
userMethod.invoke(userClass.newInstance(),"san");
}
}
2.3 Field
(属性)
• 类中的一个属性代表 Field
一个实例化
通过反射操作属性(不建议直接操作属性)
public class dome01 {
public static void main(String[] args) throws Exception {
// 通过反射操作属性(不建议直接操作属性)
// 获取Class的实例化User
Class<?> userClass = Class.forName("io.User");
// 实例化user对象,下面给属性赋值时需要
User user = (User) userClass.newInstance();
// 获取到属性为name的属性实例化
Field nameField = userClass.getDeclaredField("name");
// 运行外部访问(将private换为public)
nameField.setAccessible(true);
// 设置属性内容,需要修改的实例化对象作为参数
nameField.set(user,"lis");
System.out.println(user);
}
}
3. 通过反射操作数组
• 反射不仅可以运用在类上,还可以运用到引用数据类型上
public class dome01 {
public static void main(String[] args) throws Exception {
int[] temp={1,2,3};
// 获取到数组的Class实例化
Class<?> componentType = temp.getClass().getComponentType();
System.out.println(componentType.getName());
// 用Array获取到数组的数据
int data = (int) Array.get(temp, 0);
System.out.println(data);
}
}
4. JDK动态代理(需要接口)
• 代理主题是接口实现类
我们在静态代理中会遇到以下问题:
// 真实主题
public class Real implements NetWork{
public void browse() {
System.out.println("上网浏览信息...");
}
// 1. 当我们在这里增加方法时:add()....
}
// 代理主题
public class Proxy implements NetWork{
// 真实主题作为参数,通过传入Real真实主题(Real覆写过的父类)来获得需要进行的真实操作
private NetWork netWork;
Proxy(NetWork netWork){
this.netWork=netWork;
}
// 需要代理操作的其他处理方法
public void check(){
System.out.println("检查上网环境...");
}
// 通过调用顺序实现先处理代理主题,再实现真实主题
public void browse() {
check();
netWork.browse();
}
// 2. 这里还需要我们手动的增加代理方法add()...
}
public class dome02 {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Real());
proxy.browse();
}
}
• 这时就需要动态代理:通过绑定真实主题,来对里面的方法进行统一代理
• Proxy.newProxyInstance
对象:生成被代理标识后的对象(接口对象)
ClassLoder loader
:真实主题类加载器Class<?>[] interfaces
:得到真实主题全部的接口InvocationHandler h
:InvocationHandler
的实例
•invoke
方法:被代理的方法调用时,进行代理方法的执行和增强Object proxy
:被代理的对象(生成的接口对象)Method method
:被调用的代理方法Object[] args
:代理方法中的参数
代码示例
public interface Subject {
// 需要代理的方法
void say(String name,int age);
}
// 真实主题(对需要代理的方法进行完善)
public class RealSubject implements Subject {
public void say(String name, int age) {
System.out.println(age+",holle:"+name);
}
}
// 可以看做对真实主题进行处理
public class LogHandler implements InvocationHandler {
// 需要代理的真实主题,因为是调用处进行实时传递,所以称为动态
private Object obj;
// 根据传进来的真实主题,进行绑定(绑定标识为需要代理的主题,
// 当使用其中的方法时,进行下方invoke方法的特殊处理)
public Object bind(Object obj){
this.obj = obj;
// 返回的是通过绑定代理标识后的代理对象(接口对象Subject :通过实现接口方法,多态赋值给接口)
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
this
);
}
// 其中对代理的方法进行执行和增强
// 感受到外部调用到了需要代理的方法的时候,如下方调用处proxySubject.say("lis",18);
// proxy则是被代用的代理对象:proxySubject
// method自动为被调用的方法:say
// args自动为被调用的方法的参数:"lis",18
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强1
System.out.println("before...");
System.out.println(proxy.getClass().getName());
// 执行代理的方法
Object invoke = method.invoke(obj,args);
// 增强2
System.out.println("after...");
// 返回方法结果
return invoke;
}
}
public class dome01 {
public static void main(String[] args) throws Exception {
// 实例LogHandler对下方RealSubject进行绑定
LogHandler logHandler = new LogHandler();
// 对RealSubject进行绑定,返回的是接口的对象
Subject proxySubject = (Subject)logHandler.bind(new RealSubject());
// 调用被代理的方法;触发代理invoke方法
proxySubject.say("lis",18);
}
}
过程描述:
- 要用到动态代理,我们就要实例化一个
Proxy.newProxyInstance(...)
出来(可以看成这个就是生成代理对象) - 但是
newProxyInstance
需要一个参数为InvocationHandler
(对被代理对象的方法进行增强),所以我们要新增一个实现InvocationHandler
的类,实例化,作为参数给到Proxy.newProxyInstance
- 然后我们使用实例化的
Proxy.newProxyInstance(...)
中被代理对象的方法就可以实现动态代理了
5. CGlib动态代理
• 代理主题不是实现类
CGLIB既可以代理接口,又可以代理类。
底层采用继承的方式实现(创建一个子类来继承被代理的对象)。
所以被代理的目标类不能使用 final
修饰。
使用CGLIB,需要引入它的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
我们准备一个没有实现接口的类,如下:
package com.powernode.mall.service;
public class UserService {
public void login(){
System.out.println("用户正在登录系统....");
}
public void logout(){
System.out.println("用户正在退出系统....");
}
}
使用CGLIB在内存中为 UserService
类生成代理类,并创建对象:
package com.powernode.mall;
import com.powernode.mall.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建字节码增强器
Enhancer enhancer = new Enhancer();
// 告诉cglib要继承哪个类
enhancer.setSuperclass(UserService.class);
// 设置回调接口
enhancer.setCallback(方法拦截器对象);
// 生成源码,编译class,加载到JVM,并创建代理对象
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login();
userServiceProxy.logout();
}
}
和JDK动态代理原理差不多,在CGLIB中需要提供的不是 InvocationHandler
,而是:net.sf.cglib.proxy.MethodInterceptor
编写 MethodInterceptor
接口实现类:
package com.powernode.mall.service;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return null;
}
}
MethodInterceptor
接口中有一个方法 intercept()
,该方法有4个参数:
第一个参数:目标对象
第二个参数:目标方法
第三个参数:目标方法调用时的实参
第四个参数:代理方法
在 MethodInterceptor
的 intercept()
方法中调用目标以及添加增强:
package com.powernode.mall.service;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前增强
long begin = System.currentTimeMillis();
// 调用目标
Object retValue = methodProxy.invokeSuper(target, objects);
// 后增强
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
// 一定要返回
return retValue;
}
}
回调已经写完了,可以修改客户端程序了:
package com.powernode.mall;
import com.powernode.mall.service.TimerMethodInterceptor;
import com.powernode.mall.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建字节码增强器
Enhancer enhancer = new Enhancer();
// 告诉cglib要继承哪个类
enhancer.setSuperclass(UserService.class);
// 设置回调接口
enhancer.setCallback(new TimerMethodInterceptor());
// 生成源码,编译class,加载到JVM,并创建代理对象
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login();
userServiceProxy.logout();
}
}
对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数:
• --add-opens java.base/java.lang=ALL-UNNAMED
• --add-opens java.base/sun.net.util=ALL-UNNAMED
执行结果:
6. 通过反射完善工厂模式
原来的工厂模式问题:Factory
类中有多少个子类就需要多少个 if
判断!
通过反射完善:外部调用工厂时,传入子类的完整路径,然后工厂中通过反射创建对象
// 其余代码不变...
public class Factory {
public static Fruit getInstance(String className) throws Exception {
// 通过获取到Class的实例来实例化需要的类
Fruit fruit=(Fruit) Class.forName(className).newInstance();
return fruit;
}
}
// 调用处...
Fruit f=Factory.getInstance("io/User")
提示:可以搭配属性文件使用
我们每次实例化对象时,都需要找寻类的完整路径,过于麻烦。
我们可以将每个子类的以 子类名=子类完整路径
的方式写入到属性文件中,再通过Java对属性文件的读写来获取子类的完整路径
第16章:Annotation(注解)与Lamda
1. Annotation(注解)
• 个人理解:一种标识,让程序在运行时通过这种标识表明你的想法,操作,并让程序做出相应的操作
三种内存的注解:
@Override
:表明我要覆写方法@Deprecated
:表明该方法不建议使用@SuppressWarning
:表明不用警告
提示:@SuppressWarning
在使用时需要指定不需要警告的类型
// 压制两种警告
@SuppressWarnings({"unchecked","deprecation"})
public void setHello(String name){
System.out.println("你好,我是:"+name);
}
2. 自定义Annotation
• 注解里面设置了变量,调用时就必须要传入值,除非设置默认值
public @interface MyAnnotation {
// 数据类型 变量名称();
String value();
// 设置默认值
String name() default "san";
}
提示:
使用 @interface
就相当于继承了 Annotation
接口
// 调用自定义注解,有默认值的可以不输入,否则必须输出参数
// @MyAnnotation("123") 当需要输入的参数只有一个且是value时可以省略
@MyAnnotation(value = "123")
public void print(){}
• 可以使用枚举来限制传入的内容
public @interface MyAnnotation {
// 枚举类型 变量名称();
Color value();
}
@MyAnnotation(value = Color.BLUE)
public void print(){}
3. @Retention
和 RetentionPolicy
• @Retention
:表明自定义注解的作用范围
• RetentionPolicy
:提供的范围(由小到大)
RetentionPolicy.SOURCE
:作用于源文件中 --*.java
RetentionPolicy.CLASS
:可以在类文件中出现 --*.class
RetentionPolicy.RUNTIME
:可以在执行时出现 --JVM中运行
@Retention (RetentionPolicy.CLASS)
public @interface MyAnnotation {
// 数据类型 变量名称();
Color value();
}
4. 通过反射获取Annotation

@Retention (RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 数据类型 变量名称();
String value();
String name();
}
public class dome01 {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 1.获取类上所有的注解
// Annotation[] annotations = userClass.getAnnotations();
// 2.获取指定注解,先判断有没有
if (userClass.isAnnotationPresent(MyAnnotation.class)){
// 再指定Class类型获取
MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
// 获取自定义注解中的属性
System.out.println(annotation.name());
}
}
}
5. @Target
注解(指定注解使用位置)
• @Target
:表明注解使用位置(属性,方法,类...)
• @Target
使用在自定义注解上
// 只能作用在类,接口,枚举类型上
@Target(ElementType.TYPE)
public @interface MyAnnotation {
// 数据类型 变量名称();
String value();
String name();
}
6. @Inherited
注解(表明可以被继承)
• 表明标记在父类上的注解可以被子类继承
• @Inherited
使用在自定义注解上
@Inherited
public @interface MyAnnotation {
// 数据类型 变量名称();
String value();
String name();
}
// 父类的自定义注解
@MyAnnotation(value = "value123",name = "name123")
public class User {
private String name;
}
// 子类继承父类,后依然拥有自定义注解的特性
public class Student extends User {
private String studentName;
}
7. Lambda表达式
• 使用条件:含有一个抽象方法的接口
提示:
可以使用 @FunctionalInterface
注解申明接口为函数式接口
• 函数式接口:只有一个抽象方法的接口
提示:
• 方法主体只有一行时:(params)->方法主体
(一行中如...(3789 characters truncated)
• 方法主体只有多行时:(params)->{ 方法主体 }
public interface IMessage {
// 只能有一个方法
void print();
}
public class dome01 {
public static void main(String[] args) throws Exception {
// Lambda表达式;括号内表示我实例化了一个实现IMessage接口的类并只重写了方法,并作为参数
fun(()-> System.out.println("重新IMessage的print方法..."));
}
// 设置需要IMessage接口实例化作为参数的方法
// 正常需要多态实现接口实例化来传入参数
public static void fun(IMessage msg){
msg.print();
}
}
8. 方法引用
• 将已经存在的方法,复制给自己函数式接口中的方法,使其具有相同的功能
• 有如下4种操作形式;
- 引用静态方法:
类名称 :: 方法名称
---IMessage<String,Integer> msg = String::valueOf;
- 引用某个对象的方法:
实例化对象:: 方法名称
---IMessage<String> msg = "hello"::toUpperCase;
- 引用指定类型方法:
特定类:: 方法名称
---IMessage<String> msg = String::compareTo;
- 引用构造方法:
类名称 :: new
---IMessage<User> msg = User::new;
// 函数式接口
@FunctionalInterface
public interface IMessage<R> {
R print();
}
public class dome01 {
public static void main(String[] args) throws Exception {
// 将"hello"字符串String对象的toUpperCase方法复制到IMessage<String>的print方法中
IMessage<String> msg = "hello"::toUpperCase;
String print = msg.print();
System.out.println(print);
}
}
/*
HELLO
*/
提示:
注意基座问题,上述代码中使用 hello
对象来复制转换大写方法,则运行时的对象就只能为“hello”
9. 内建函数式接口
• 当我们建立函数式接口的时候就只有,有参有返,有参无返,无参有返,判断真假4种。所有Java将其格式给固定化,用于可以直接调用,不用在建立函数式接口
例如上面小结8中的代码就可以不用自定义接口直接使用:
public class dome01 {
public static void main(String[] args) throws Exception {
// 将"hello"字符串String对象的toUpperCase方法复制到IMessage<String>的print方法中
// 直接使用Supplier接口复制给其中抽象方法get()并使用
Supplier<String> msg = "hello"::toUpperCase;
String print = msg.get();
System.out.println(print);
}
}