反射
1. 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性)
2.
JDBC为什么要使用反射:如果使用new com.mysql.jdbc.Driver()这种方式,会对这个具体的类产生依赖。后续如果你要更换数据库驱动,就得重新修改代码。而使用反射的方式,只需要在配置文件中,更改相应的驱动和url即可
3. 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
Class对象的由来是将class文件读入内存,并为之创建一个Class对象
Java中编译类型有两种:
1. 静态编译,在编译时确定类型,绑定对象即通过
2. 动态编译,运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性
3. 反射允许静态语言在运行时检查、修改程序的结构与行为
反射Class的使用
Person p1 = new Person();
// 1 对象的getClass方法
Class c1 = p1.getClass();
System.out.println(c1);
// 2 类的class属性
System.out.println(Person.class);
// 3 Class.forName
try {
Class c = Class.forName("Person");
System.out.println(c);
Field f = c.getField("age");
System.out.println(f);
}catch (Error e){
e.printStackTrace();
}
Test test = new Test();
Class c4 = test.getClass();
// 获取类的名字
String className =c4.getName();
System.out.println(className);
// 获取所有public的字段
List fieldsPublic = Arrays.asList(c.getFields());
System.out.println(fieldsPublic);
// 获取所有字段
List fieldsAll = Arrays.asList(c.getDeclaredFields());
System.out.println(fieldsAll);
// 获得类的public类型的方法 包含继承下来的
Method[] methods = c.getMethods();
// 获取类自己的,所有声明的方法
Method[] methods = c.getDeclaredMethods();
// 获取指定名字的方法
Method method = c.getMethod("gg");
System.out.println(method);
Constructor[] constructors;
// 获取类的所有构造方法,是一个数组,因为构造方法不止一个
constructors = c4.getDeclaredConstructors();
// 索引为1的构造方法
Constructor c1 = constructors[1];
System.out.println(c1);
// 构造方法的修饰符
System.out.println(Modifier.toString(c1.getModifiers()));
// 构造方法的参数
Class[] args1 = c1.getParameterTypes();
System.out.println(args1[0]);
// 获取指定构造方法并执行
Class[] p = {int.class, String.class};
constructors = c4.getDeclaredConstructor(p);
// 通过类的不带参数的构造方法创建这个类的一个对象
constructors.newInstance("huahua", 12);
代理
为其他对象提供一种代理以控制对这个对象的访问
1、静态代理
被代理对象与代理对象需要一起实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类.
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护
public class Proxy1 {
public static void main(String[] args) {
// 创建明星对象
IStar ldh = new LDHStar();
ProxyManger proxy = new ProxyManger(ldh);
proxy.sing();
}
}
interface IStar {
void sing();
}
// 真实对象
class LDHStar implements IStar {
@Override
public void sing() {
System.out.println("刘德华唱歌");
}
}
// 代理类需要有真实对象的控制权 (引用)
class ProxyManger implements IStar {
// 真实对象的引用
private IStar star;
public ProxyManger() {
super();
}
public ProxyManger(IStar star) {
super();
this.star = star;
}
@Override
public void sing(){
System.out.println("唱歌准备");
star.sing();
System.out.println("唱完了");
}
}
2 、动态代理
动态代理的主要特点就是能够在程序运行时JVM才为被代理对象生成代理对象。 常说的动态代理也叫做JDK代理也是一种接口代理,JDK中生成代理对象的代理类就是Proxy,包是java.lang.reflect
枚举
枚举类就是class,而且是一个不可以被继承的final类,它的值都是常量,所以最好大写
public class enum1 {
public static void main(String[] args) {
Color c = Color.BLANK;
System.out.println(c);
System.out.println(c.getIndex());
System.out.println(c.getName());
}
}
enum Color {
RED("红色", 1),
GREEN("绿色", 2),
BLANK("白色", 3),
YELLO("黄色", 4),
PURPLE("粉色", 5);
// 成员变量
private String name;
private int index;
// 构造方法
Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}
泛型
1. 泛型就是参数化类
2. Java中的泛型,只在编译阶段有效,泛型信息不会进入到运行时阶段
3. 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
// 泛型类
class Foo<T> {
private T name;
Foo(T name){
this.name = name;
}
public T getName() {
return name;
}
}
泛型接口
interface jiekou1<T> {
T getName();
}
// 接口使用了泛型,类必须要用
class lei1<T> implements jiekou1<T>{
@Override
public T getName() {
return null;
}
}
泛型通配符
public static void main(String[] args) {
Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);
showKeyValue1(gNumber);
showKeyValue1(gInteger);//报错
}
static void showKeyValue1(Generic<Number> obj){
System.out.println(obj.getKey());
}
// 把参数的类型,变成? 就成了类型通配符
static void showKeyValue1(Generic<?> obj){
System.out.println(obj.getKey());
}
泛型方法
// 泛型方法和可变参数
public static <T> void printMsg( T... args){
for(T t : args){
System.out.println("泛型测试" + "t is " + t);
}
}
静态方法,静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上 泛型边界
// 只能是Number子类的数据
public void showKeyValue1(Generic<? extends Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
class Generic<T extends Number>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
数组泛型
List<?>[] list = new ArrayList<?>[10];
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
list[0] = list1;
list[1] = list2;
list1.add("haha");
System.out.println(list[0]);
多线程
1. 进程是系统进行资源分配和调度的一个单位
2. 当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程
3. 现在操作系统的并发性主要采用,抢占式多任务操作策略
4. 线程可以完成一定任务,可以和其它线程共享父进程的共享变量和部分环境,相互协作来完成任务
5. 线程是独立运行的,其不知道进程中是否还有其他线程存在
6. 线程的执行是抢占式的,也就是说,当前执行的线程随时可能被挂起,以便运行另一个线程。一个线程可以创建或撤销另一个线程,一个进程中的多个线程可以并发执行
7. 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行), 多核CPU来说,可以真正做到微观并行
进程的特点
1. 独立性,进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间
2. 动态性,进程和程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态
3. 并发性,多个进程可以在单个处理器上并发执行,互不影响
并行和并发
并行是指同一时刻,多个命令在多个处理器上同时执行
并发是指同一时刻,只有一条命令是在处理器上执行的,但多个进程命令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果
java线程的实现
1. 是继承Thread类,然后重写run方法
public class Thread1 {
public static void main(String[] args) {
MyThread t = new MyThread("线程-1 ");
t.run();
}
}
class MyThread extends Thread {
private String name;
public MyThread(String name1) {
this.name = name1;
}
@Override
public void run() {
int a = 4;
while(--a > 0){
System.out.println(this.name + a);
}
}
};
2. 实现Runnable接口,然后实现其run方法
public class Thread1 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程A ") ; // 实例化对象
MyThread mt2 = new MyThread("线程B ") ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类
private String name ; // 表示线程的名称
public MyThread(String name){
this.name = name ; // 通过构造方法配置name属性
}
@Override
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<10;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
3. 将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类
4. 生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)
线程的生命周期
1.
创建状态 当用new操作符创建一个新的线程对象时,该线程处于创建状态。处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
2.
可运行状态 执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体run()方法,这样就使得该线程处于可运行( Runnable )状态。这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行
3.
不可运行状态 当发生下列事件时,处于运行状态的线程会转入到不可运行状态,调用了sleep()方法线程调用wait方法,等待特定条件的满足,线程输入/输出阻塞
4.
返回可运行状态 处于睡眠状态的线程在指定的时间过去后 ,如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变,如果线程是因为输入/输出阻塞,等待输入/输出完成
5.
消亡状态 当线程的run方法执行结束后,该线程自然消亡
note
1. 停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束
2.不能依靠线程的优先级来决定线程的执行顺序
synchronized
1. java多线程为了避免多线程并发解决多线程共享数据同步问题提供了synchronized关键字
2. synchronized关键字修饰一个方法的时候,该方法叫做
同步方法
3. Java中的每个对象都有一个
锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法
4. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的
5. 如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行
6. synchronized块
synchronized(object) {
}
表示线程在执行的时候会对object对象上锁
7. synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的
wait与notify
1. wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁
2. 这两个方法的调用需要放在synchronized方法或块当中
3. 另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的
4. wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠
5. notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行
6.
线程可以成为此对象监视器的所有者的方法 一,通过执行此对象的同步实例方法。二, 通过执行在此对象上进行同步的 synchronized 语句的正文。三,对于 Class 类型的对象,可以通过执行该类的同步静态方法。一次只能有一个线程拥有对象的监视器
7.
对成员变量和局部变量的影响 如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。 如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
死锁
当多个并发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程必需的锁,互相等待时被阻塞就有可能出现死锁
ThreadLocal
- ThreadLocal是除了加锁这种同步方式之外的,一种保证一种规避多线程访问出现线程不安全的方法,
- 当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题
- ThreadLocal不继承,InheritableThreadLocal可以继承
public class ThreadLocalTest {
static ThreadLocal<String> localVar = new ThreadLocal<>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localVar.get());
//清除本地内存中的本地变量
localVar.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("localVar1");
//调用打印方法
print("thread1");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("localVar2");
//调用打印方法
print("thread2");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
});
t1.start();
t2.start();
}
}