记一些Java知识或应用
反射
getClass()
能从一个实例化对象得到一个Class类对象
Class类对象的三种实例化模式:
1. Object类可以根据实例化对象获取Class对象:Class<? extends Person> cls = person.getClass();
2. 采用类.class的方式实例化:Class<? extends Person> cls = Person.class;
3. 使用Class类的静态方法:Class<?> cls = Class.forName("包路径");
使用这种方法不需要导入对应的包,但包必须存在。
通过反射得到的实例化对象(Object obj = cls.newInstance();
)本质是调用类中的无参构造方法。此方法在JDK1.9后被遗弃。随后新的方法是 cls.getDeclaredConstructor().getInstance();
其本质则是获取构造方法的实例。
应用案例
工厂模式。比如现在有一个接口,接口给很多层提供一个同一的操作标准,因此有很多种实现。如果我在操作中去new一个实现的实例,那么就出现了耦合。
interface Message(){
public void send();
}
class NetMessage implements Message{
public void send(){
sout("YYY");
}
}
class StatisMessageFactory(){ //静态工厂设计模式,此时如果增加子类,那么只能修改代码
private Factory(){}
public static Message getInstance(String className){
if("netmessage".epualsIgnoreCase(className)) return new NetMessage();
else return null;
}
}
静态工厂的缺陷在于,他创建实例的时候需要有一个明确的类存在,即需要去new获取实例。因此修改if体为 Class.forName(className).getDeclaredConstructor().newInstance()
但是开发中不可能只有一个接口,工厂也不应该只为一个接口服务。那么:
class Factory(){
private Factory(){}
public static <T> T getInstance(String className, Class<T> clazz){
T instance = null;
try{
instance = (T) Class.forName(className).declaredConstructor().newInstance();
}
return instance;
}
}
单例模式。懒汉式、饿汉式。。。
懒汉式
class Singleton{
private static Singleton instance = null;
private Singleton(){
sout("*********** 创建了Singleton实例化对象 ***********");
}
public static Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
public void print(){sout("嘤嘤嘤");}
}
public static void main(){
for(int x=0; x<3; x++){
new Thread(()->{
Singleton.getInstance().print();
}, "ThreadName-"+x).start();
}
}
上面的代码会产生三个实例化,这违背了单例的设计理念。其产生原因是getInstance()时操作不同步。对其声明synchronized
能够解决这个问题,但是又会使之效率低下。
private static volatile Singleton instance = null;//与主内存中的对象保持同步而不是存副本
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null) instance = new Singleton();
}
}
return instance;
}
反射与类操作
获取类的基本信息。
一个类的主要信息包括所在的包名称、父类的定义、父接口的定义。
1. 获得包名称:Class<?> cls = person.class; cls.getPackage().getName();
2. 获得父类:cls.getSuperclass()
得到的也是一个Class对象
3. 获得父接口:cls.getInterfaces()
数组
因此,一个Class对象能够描述一个完整的继承关系
反射调用构造方法
- 获取所有构造方法:
Constructor<?>[] getConstructors()
- 获取指定构造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)
- 获取所有构造方法:
Constructor<?>[] getDeclaredConstructors()
- 获取指定构造方法:
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
调用指定构造实例化对象:
虽然可以使用有参构造处理,但仍建议所有使用反射的类具备无参构造来达到统一。
获取方法对象。
反射调用对象方法:Object invoke(Object obj, Object... args);
范例(在不导包的情况下调用类方法):
获取类成员(Filed
范例:
临时总结:
getDeclaredxxx()
能获取本类的所有字段,包括私有,当然能不能用是另一回事。getxxx()
能够获取类整个继承关系下的所有公有字段。
解除封装:.setAccessible(true);
反射与简单JAVA类(Bean类
引言:按照传统编程方式,即获得对象、设置属性、使用对象,会在设置属性一步存在大量的重复操作。而反射机制的最大特征就是根据其直接操作属性或方法的特性,来实现相同功能类的重复操作的抽象处理。
设置单级属性
public class API {
public static void main(String[] args) throws Exception {
String value = "name:yyy|job:sleeper";
Emp emp = ClassInstanceFactory.create(Emp.class, value);
if(emp != null ) System.out.println(emp.getName()+" "+emp.getJob());
}
}
class ClassInstanceFactory{
private ClassInstanceFactory(){}
/**
* 实例化对象的创建方法
* @param clazz 实例化对象类型
* @param value 实例的值设置("key:value")
* @param <T> 实例化对象类型
* @return 实例化对象
*/
public static <T>T create(Class<?> clazz, String value) throws Exception{
// 要求实例化对象类中必须要有无参构造,否则必定报错
Object obj = clazz.getDeclaredConstructor().newInstance();
BeanUtils.setValue(obj, value);
return (T) obj;
}
}
class StringUtils{ //首字母大写
private StringUtils(){}
public static String initcap(String src){
if(src == null || "".equals(src)){
return src;
}else if(src.length() == 1)
return src.toUpperCase();
else
return src.substring(0, 1).toUpperCase() + src.substring(1);
}
}
class BeanUtils{ //Bean处理类:添加属性
private BeanUtils(){
}
/**
* 实现指定对象的属性设置
* @param object 要进行反射操作的实例对象
* @param value 包含有指定格式的字符串
*/
public static void setValue(Object object, String value){
String [] results = value.split("\\|");
for(String result:results){
String [] attr_val = result.split(":");
String attr = attr_val[0]; String val = attr_val[1];
try{
Field field = object.getClass().getDeclaredField(attr);
Method setMethod = object.getClass().getDeclaredMethod("set"+StringUtils.initcap(attr), field.getType());
setMethod.invoke(object, val);
} catch (Exception e){
e.printStackTrace();
}
}
}
}
class Emp{
String name;
String job;
/*setter getter*/
}
多数据类型
/**
* 将字符串表示的值转换成对应的数据类型
* @param type
* @param val
* @return
*/
private static Object parseVal(String type, String val){
if("int".equals(type) || "java.Long.Integer".equals(type)) {
return Integer.parseInt(val);
}
else return null; //后续类推
}
级联对象( dept.company.name)
public class ClassTest_Person {
public static void main(String[] args) throws Exception {
String value = "name:yyy|job:sleeper|salary:8008820|date:1999-05-27|dept.company.name:yyy";
Emp emp = ClassInstanceFactory.create(Emp.class, value);
if(emp != null ) System.out.println(emp);
// System.out.println(Double.class.getName());
}
}
class ClassInstanceFactory{
private ClassInstanceFactory(){}
/**
* 实例化对象的创建方法
* @param clazz 实例化对象类型
* @param value 实例的值设置("key:value")
* @param <T> 实例化对象类型
* @return 实例化对象
*/
public static <T>T create(Class<?> clazz, String value) throws Exception{
// 要求实例化对象类中必须要有无参构造,否则必定报错
Object obj = clazz.getDeclaredConstructor().newInstance();
BeanUtils.setValue(obj, value);
return (T) obj;
}
}
class StringUtils{ //首字母大写
private StringUtils(){}
public static String initcap(String src){
if(src == null || "".equals(src)){
return src;
}else if(src.length() == 1)
return src.toUpperCase();
else
return src.substring(0, 1).toUpperCase() + src.substring(1);
}
}
class BeanUtils{ //Bean处理类:添加属性
private BeanUtils(){}
/**
* 实现指定对象的属性设置
* @param object 要进行反射操作的实例对象
* @param value 包含有指定格式的字符串
*/
public static void setValue(Object object, String value){
String [] results = value.split("\\|");
for(String result:results){
String [] attr_val = result.split(":");
String attr = attr_val[0]; String val = attr_val[1];
try{
String [] attrs = attr.split("\\.");
Object curObj = object; int x;
for(x=0; x<attrs.length-1; x++){
Field field = curObj.getClass().getDeclaredField(attrs[x]);
if(field.get(curObj) != null) curObj = field.get(curObj);
else{
Method setMethod = curObj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attrs[x]), field.getType());
Object newObj = field.getType().getDeclaredConstructor().newInstance();
setMethod.invoke(curObj, newObj);
curObj = newObj;
}
}
Field field = curObj.getClass().getDeclaredField(attrs[x]);
Method setMethod = curObj.getClass().getDeclaredMethod("set"+StringUtils.initcap(attrs[x]), field.getType());
Object parsedVal = parseVal(field.getType().getName(), val);
setMethod.invoke(curObj, parsedVal);
} catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 将字符串表示的值转换成对应的数据类型
* @param type
* @param val
* @return
*/
private static Object parseVal(String type, String val) {
if(Integer.class.getName().equals(type) || int.class.getName().equals(type)) {
return Integer.parseInt(val);
}else if(Double.class.getName().equals(type) || double.class.getName().equals(type)){
return Double.parseDouble(val);
}else if(Date.class.getName().equals(type)){
SimpleDateFormat simpleDateFormat = null;
if(val.matches("//d{4}-//d{2}-//d{2}")){ // yyyy-MM-dd
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
}else if (val.matches("//d{4}-//d{2}-//d{2} //d{2}://d{2}://d{2}")){
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}else return new Date();
try {
return simpleDateFormat.parse(val);
} catch (ParseException e) {
return new Date();
}
}
else return val; //后续类推
}
}
ClassLoader-类加载器
java语言有一个系统的环境变量CLASSPATH
,这个变量的作用是在JVM进程启动的时候进行类加载路径的定义。
类加载器可以用来实现类反射加载
打扰了 不看了
代理模式
所谓代理模式,是指客户端(Client)并不直接调用实际的对象(下图右下角的RealSubject),而是通过调用代理(Proxy),来间接的调用实际的对象。
上面的代理设计模式属于静态代理,缺点是代理类与实体类之间存在耦合,即每一个实体类都需要一个对应的代理类。
动态代理的设计:
- 代理类:要能够接收所有的真实业务子类;因为不再和具体 接口捆绑,因此要能获取具体接口信息。
- 在进行动态代理设计的时候动态对象的创建时由JVM底层完成的,此时主要依靠的时
java.lang.Proxy
程序类,而这个程序类之中只提供有一个核心方法- 代理对象:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
- loader: 获取当前主题类的ClassLoader;
- interfaces: 代理是围绕接口进行的,所以一定要获取真实主题类的接口信息;
- h: 代理处理的方法。
package study;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Message{ //传统代理模式必须有接口
public void send(); //业务方法
}
class MessageReal implements Message{
@Override
public void send() {
System.out.println("发送消息");
}
}
class AllProxy implements InvocationHandler {
private Object target; //保留真实业务主体对象
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 代理方法调用,代理主题类最终要执行的都是此方法
* @param proxy 要代理的主题
* @param method 要执行的方法名称
* @param args 传递的参数
* @return 某一个方法的返回值
* @throws Throwable 方法调用的错误向上抛出
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[执行方法]"+method.getName());
return method.invoke(proxy, args);
}
}
public class dailimoshi {
public static void main(String[] args) {
Message message = (Message) new AllProxy().bind(new MessageReal());
message.send();
}
}
局限性:如果被代理的类未实现任何接口,那么不能采用通过InvocationHandler动态代理的方式去代理它的行为。
、
通过CGLIB包实现基于类的代理:
异常与错误
异常是语法或语义上的错误,是开发人员导致的
错误通常是JVM的一个故障
综上,我们可以知道异常和错误最本质的区别就是异常能被开发人员处理而错误时系统本来自带的,一般无法处理也不需要我们程序员来处理。
异常的结构分类
- 运行时异常(未检查异常)不强制用try{}catch{}检查,比如数组越界、除零、空指针
- 编译时异常(已检查异常)强制捕获
一般情况下,不要捕获或声明RuntimeException。因为问题在于你的程序本身有问题,如果你用异常流程处理了,反而让正常流程问题一直存在
即使catch语句中有return,finally中的内容也会被执行
如果finally中也有异常,原有异常会被压制
使用JWT实现token验证
JWT分为三个部分,以.
分隔
- header
- payload
- signature
header
eg: Base64.encode(“{“typ”:”JWT”,”alg”:”HS256”}”)
payload
eg: Base64.encode(“// JSON格式的一些标准声明(签发者、签发时间、过期时间等)和自定义声明”)
signature
header中声明的算法(“header.payload”, secret),secret由服务器持有,为私钥
信号量
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();//使用并发库,创建缓存的线程池
final Semaphore sp = new Semaphore(3);//创建一个Semaphore信号量,并设置最大并发数为3
//availablePermits() //用来获取当前可用的访问次数
System.out.println("初始化:当前有" + (3 - sp.availablePermits() + "个并发"));
//创建10个任务,上面的缓存线程池就会创建10个对应的线程去执行
for (int index = 0; index < 10; index++) {
final int NO = index; //记录第几个任务
Runnable run = new Runnable() { //具体任务
public void run() {
try {
sp.acquire(); // 获取许可
System.out.println(Thread.currentThread().getName()
+ "获取许可" + "("+NO+")," + "剩余:" + sp.availablePermits());
Thread.sleep(1000);
// 访问完后记得释放 ,否则在控制台只能打印3条记录,之后线程一直阻塞
sp.release(); //释放许可
System.out.println(Thread.currentThread().getName()
+ "释放许可" + "("+NO+")," + "剩余:" + sp.availablePermits());
} catch (InterruptedException e) {
}
}
};
service.execute(run); //执行任务
}
service.shutdown(); //关闭线程池
}
}
// from https://blog.csdn.net/eson_15/article/details/51577191
等待线程结束
ThreadPoolExecutor pool = new ThreadPoolExecutor(...);
for(var task : tasks) pool.execute(task);
pool.shutdown();
pool.awaitTermination(3, TimeUnit.MILLISECONDS);