JAVA反射机制
已经加载过这个类,通过类名来找到这个类的所有相关信息。
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关的主要API:
- java.lang.Class : 代表一个类
- java.lang.reflect.Method : 代表类的方法
- java.lang.reflect.Field : 代表类的成员变量
- java.lang.flect.Constructor : 代表类的构造方法
// 获取Class对象
Class clazz = p.getClass(); // clazz里面包含了对象p所属类的有关信息
Class c1 = Person.class; // 通过类名.class创建指定类的Class实例
Class c2 = Class.forName("包名.类名"); // 这个比较常用
// 获取
clazz.getName(); // 获取名称
clazz.getModifiers(); // 获取修饰符
Class[] paramClazz = clazz.getParameterTypes(); // 获取参数类型
Class superClazz = clazz.getSuperClass(); // 获取父类
Class[] interfaces = clazz.getInterfaces(); // 获取接口
Constructor[] cons = clazz.getConstructors(); // 获取public构造方法
Constructor[] cons = clazz.getDeclaredConstructors(); // 获取所有构造方法
// 动态调用构造器,创建对象,得到的都是Object,需要强制转换
Object obj = clazz.newInstance(); // 调用无参构造
Constructor con = clazz.getConstructor(String.class);
//获得参数为String的构造器。用DeclaredConstructors()可以获取private的构造方法。但是要在前面加一句,con.setAccessible(true); 解封装
Object obj = con.newInstance("lzq"); // 调用有参构造
// 动态获取方法、属性进行操作. 对私有属性、方法操作是,注意解封装
Method[] ms = clazz.getMethods(); // 获得全部公有方法,要获得私有的和上同理。
Field[] fs = clazz.getFiedls(); // 获取属性
Package p = clazz.getPackage(); // 获取包名
// 得到名称叫name 参数是String String 的方法。
Method m1 = clazz.getMethod("name",String.class,String.class);
m1.invoke(obj, args); // 参数1是需要实例化的对象,通过前面的构造可以得到,参数2是可变长的前面方法的实际参数,如果调用的方法有返回值,就接受并强制类型转换。
Field f = clazz.getFiedl("属性名");
f.set(stu, "aaaa"); // 设置stu对象的f属性值为aaaa
f.get(stu); // 获取stu对象的f属性值,接收时注意类型转换。
// java 动态代理。注意 一个对象想要被代理,一定要有接口。
class ProxyOfTest implements InvocationHandler{
Object obj; // 被代理的对象
public ProxyOfTest(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
// 此处 在指定方法调用前,动态添加行为
Object result = method.invoke(this.obj, args); //执行的是代理对象的制定方法。
// 此处 在指定方法调用后,动态添加行为
return result;
}
}
InvocationHandler handler = new ProxyOfTest(test); //对test对象进行代理,得到代理对象hander
Test t = (Test)Proxy.newProxyInstance(handler.getClass().getClassLoader(), test.getClass().getInterfaces(), handler); // 返回的是Object类型,是成功代理后的对象。
t.test();
Java线程
Java.lang.Thread类
- run()
- start()
/*创建线程*/
class TestThread extends Thread{ //继承Thread类的方式创建多线程
@Override
public void run() {
// 多线程运行的代码
}
}
Thread t0 = new TestThread();
t0.start(); // 启动线程,运行run方法,是异步执行
class TestRunnable implements Runnable{ // 实现Runnable接口
@Override
public void run() {
// 多线程运行的代码
}
}
Thread t1 = new Thread(new TestRunnable());
Thread t2 = new Thread(new TestRunnable(), "t2");// 也可以通过这个方式,第二个参数是线程名称。
t1.start();
使用Runnable接口实现,避免了单继承的局限性,使得多个线程可以共享一个接口实现对象,处理同一份资源。
一些常用的方法:
String name = t.getName(); // 返回线程的名称 默认为Thread-0,Threa-1等
t.setName("name"); // 设置线程名称
/*线程优先级:1-10; 数字越大越高,默认为5*/
int t.getPriority();
t.setPriority(int);
Thread.yeild(); // 线程让步:暂停自己的执行,把执行机会让给别的线程
Thread.sleep(1000); // 当前线程睡眠1000ms。
t.join(); // 相当于把t的run方法插入到这个位置。阻塞了当前的main方法,先执行join进来的线程的代码
t.stop(); // 强制停止
boolean t.isAlive(); // 判断当前线程是否还活着。
线程同步
-
使用同一个同步锁的才能实现线程同步。
-
在public方法上加上关键字 synchronized 同步锁。锁的是某个对象不是仅仅某个方法。不同的对象是不同的同步锁。
-
在static方法上加synchronized 同步锁。所有的实例化对象都是同一个同步锁。
-
用 synchronized(this){ } 来锁某个代码块。如果在其他方法中也有synchronized(this)锁住的代码块,它们使用的是同一个同步锁。
-
class A{ public void test(A a) { synchronized(a){ // 这样子可以实现不同对象有不同的锁 } } }
如果针对对象要加同步锁,那就加在方法上。
如果针对某一段代码需要加同步锁,那就直接在代码块上加同步锁。
线程通信
// a 为临界资源
a.wait(); // 阻塞
a.notify(); // 唤醒
a.notifyAll();
// 这三个方法只能用在有同步锁的方法里面
生产者和消费者
public class Test3{
public static void main(String[] args){
Clerk c = new Clerk();
// 生产者
new Thread(new Runnable(){
@Override
public void run(){
synchronized (c) {
while(true){
if(c.productNum == 0){
// 生产产品
c.productNum++;
c.notify; // 唤醒消费者
}else{
c.wait(); // 让生产者等待
}
}
}
}
},"生产者").start();
// 消费者
new Thread(new Runnable(){
@Override
public void run(){
synchronized (c) {
while(true){
if(c.productNum > 0){
// 消费产品
c.productNum--;
c.notify; // 唤醒生产者
}else{
c.wait(); // 让消费者者等待
}
}
}
}
},"消费者").start();
}
}
public class Clerk{
public static int productNum = 0;
}