Java
1、Java Reflection
jvm已经加载过这个类,给jvm一个类名,可以知道类的具体信息。
电脑的反射机制,就是通过一个抽象的类名能够在加载类的内存中找到相匹配的具体信息。
Reflection反射是被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
理解反射机制可以帮助学习框架技术。
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl、Python、Ruby是动态语言,C++、Java、C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制——Reflection(反射),用在Java身上指的是可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体(newInstance)或对其fields设值,或唤起(invoke)其methods方法。
反射机制提供的功能
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的成员变量和方法;
生成动态代理;
反射相关的主要API:
java.lang.Class:代表一个类;
java.lang.reflect.Method:代表类的方法;
java.lang.reflect.Field:代表类的成员变量;
java.lang.reflect.Constructor:代表类的构造方法;
Class类
在Object类中定义了方法,public final Class getClass() ,此方法将被所有子类继承,返回值是一个Class类,此类是Java反射的源头,实际上反射从程序的运行结果来看也很好理解,可以用个对象反射求出类的名称。
反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。
Class本身也是一个类。Class对象只能由系统建立对象;一个类在JVM中只会有一个Class实例;一个Class对象对应的是一个加载到JVM中的一个.class文件;每个类的实例都会记得自己是由哪个Class实例所生产的;通过Class可以完整地得到一个类中的完整结构。
常用方法:
实例化Class类对象的四种方法:
前提:若已知具体的类,通过类的class属性获取,此方法最为安全可靠,程序性能最高
实例:Class clazz = String.class;
前提:已知某个类的实例,调用该实例的getClass()获取Class对象
实例:Class clazz = “jklboo”.getClass()
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”)
其他:ClassLoader cl = this.getClass().getClassLoader();
Class clazz1 = cl.loadClass(“类的全类名”)
2、Java动态代理
Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类;
创建一个动态代理类所对应的Class对象;
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)//直接创建一个动态代理对象,这个对象一定要有相应的接口
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。
3、线程
程序、进程、线程
**程序(program)**是为了完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象;
**进程(process)**是程序的一次执行过程,或是正在运行的一个程序,程序是静态的,进程是动态的。
线程(thread),进程可以进一步细化为线程,是一个程序内部的一条执行路径,若一个程序可以同一时间执行多个线程,就是支持多线程的。
Java中多线程的创建和启动
本质都是通过java.lang.Thread类来实现。
Thread类的特性:
- 每个线程都是通过某个特定Thread对象的run()方法俩完成操作的,经常把run()方法的主体成为线程体;
- 想要在开启的多线程中运行的代码逻辑就写到run方法中;
- 通过该Thread对象的start()方法来调用这个线程;
构造方法:
- Thread():创建新的Thread对象;
- Thread(String threadname):创建线程并指定线程实例名;
- Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法;
- Thread(Runnable target,String name):创建新的Thread对象;
创建线程的两种方法:
继承Thread类
- 定义子类继承Thread类;
- 子类中重写Thread类中的run方法;
- 创建Thread子类对象,即创建了线程对象;
- 调用线程对象start方法 :启动线程,调用run方法;
实现Runnable接口
- 定义子类,实现Runnable接口;
- 子类中重写Runnable接口中的run方法;
- 通过Thread类含参构造器创建线程对象;
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中;
- 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法;
实现线程的第三种方法:
FutureTask方式,实现Callable接口,这种方式实现的线程可以获取线程的返回值;
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
继承Thread与实现Runnable接口区别:
继承Thread类:线程的代码存放在Thread子类的run方法中,重写run方法;
实现Runnable接口:线程代码存放在接口的子类的run方法中,实现run方法;
实现的好处:
避免了单继承的局限性;
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源;
Runnable和Callable的区别
(1) Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
(2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3) call方法可以抛出异常,run方法不可以。
(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法。
Thread类的有关方法:
void start():启动线程,并执行对象的run方法;
run():线程在被调度时执行的操作;
String getName():返回线程名称;
void setName():设置该线程的名称;
static currentThread():返回当前线程;
线程的优先级控制
线程创建时继承父线程的优先级;
MAX_PRIORITY(10);
MIN_PRIORITY(1);
NORM_PRIORITY(5);
getPriority():返回线程优先级;
setPriority(int newPriority):改变线程的优先级;
static void yield():线程让步;
join():当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止;
static void sleep(long mills):令当前活动线程在指定时间段内放弃对CPU的控制,是其他线程有机会被执行,时间到了之后重新排队;
stop():强制结合线程生命期;
boolean isAlive():判断线程是否还活着;
线程的生命周期
JDK中用Thread.State枚举表示了线程的几种状态:
-
新建态:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态;
-
就绪态:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时已具备了运行的条件;
-
运行态:当就绪的线程被调度并获得处理器资源时,便进入运行状态;
-
阻塞态:在某种特殊情况下,被挂起或进行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态;
-
死亡:线程完成了它党的全部任务或者被提前强制性的终止;
线程的同步
多个线程执行的不确定性引起执行结果的不稳定;
通过synchronized同步锁解决问题;
在普通方法上加同步锁,锁的是当前的整个对象,不是某一个方法,不同的对象就是不同的锁,线程使用不同的此方法的对象,还是有共享资源的同步问题;
如果是静态的方法,加上同步锁,那么对于所有的对象,就是一个同步锁;
如果是针对对象要加同步锁,那就加在方法上,如果针对某一段代码需要加同步锁,直接在代码块上加同步锁;
//synchronized可以放在方法声明中,表示整个方法为同步方法
public (static)synchronized void method(){
//...
}
//第二种方式
synchronized(对象){
//需要被同步的代码
}
线程的死锁问题:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁;
解决办法:
专门的算法、原则,比如加锁顺序一致;
尽量减少同步资源的定义,尽量避免锁为释放的场景;
线程的通信
java.lang.Object提供三个方法用于线程通信,这三个方法只有在synchronized方法或其代码块中才能使用,否则就会报出java.lang.illegalMonitorStateException异常;
**wait():**令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问;
**notify():**唤醒正在排队的等待同步资源的线程中优先级最高者结束等待;
**notifyAll():**唤醒正在排队等待资源的所有线程结束等待;