JAVA学习笔记(下)
多线程
创建多线程一共有四种方式!!:
- 继承Thread类
- 实现Runndable接口
- 实现Callable接口
- 使用线程池
继承Thread类
/*
创建线程的方式:方式一:继承Thread类
1.创建一个继承于Thread类的紫烈
2.重写Thread类的run() -->将此线程的操作声明在run()中
3.创建Thread类子类的对象
4.通过此对象调用start()
*/
//1.创建一个Thread类的子类
class MyThread extends Thread{
//2.重写Thread类中的run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if (i%2==0){
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3.创建Thread类子类的对象
MyThread myThread = new MyThread();
//4.通过此对象调用start():1.启动当前线程 2.调用当前线程的run()
myThread.start();
System.out.println("hello");
//也可以写匿名子类
new Thread(){
@Override
public void run() {
}
}.start();
}
}
线程常用方法
/**
* 测试Thread类中的常用方法:
* 1.start():启动当前线程,调用当前线程的run()
* 2.run():通常需要重写Thread类中的此方法,将创建的线程执行的操作声明在此方法中
* 3.currentThread():静态方法,返回执行当前代码的线程
* 4.getname():获取当前线程的名字
* 5.setname():设置当前线程的名字
* 6.yield():释放当前CUP的执行权
* 7.join():在线程A中调用线程B的join()方法,此时线程A就进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态
* 8.stop():已过时。当执行此方法时,强制结束当前线程。
* 9.sleep(long millitime):让当前线程睡眠指定millitime毫秒数,应用场景:新年倒计时的时候
* 10.isAlive():判断当前线程是否还存活
*
* 1.线程的优先级:
* MAX_PRIORITY:10
* MIN_PRIORITY:1
* NORM_PRIORITY:5 -->默认的优先级
* 2.如何获取或设置当前线程的优先级:
* getPriority():获取线程的优先级
* setPriority(int p)设置线程的优先级
* 说明:高优先级的线程要抢占低优先级的cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有高优先级的执行完后,低优先级的才执行
*/
class HelloThread extends Thread{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+":"+i);
}
if (i%20==0){
this.yield();
}
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread();
h1.setName("线程一");
//设置分线程的优先级
h1.setPriority(Thread.MAX_PRIORITY);
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
for (int i = 0; i <100 ; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+":"+i);
}
if (i==20){
try {
h1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(h1.isAlive());
}
}
实现Runnable接口
/**
* 创建多线程的方式二:实现Runnable接口
* 1.创建一个实现Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run()
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Threa类的对象,调用start()方法
*/
class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if (i%2==0){
System.out.println(i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread thread = new Thread(mThread);
thread.start();
}
}
比较创建多线程的两种方式。
开发中:优先选择实现Runnable接口的方式
-
原因:1.实现方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程,有共享数据的情况
-
联系:Thread()类也实现了Runnable接口
-
相同点:都需要重写run()方法
线程生命周期
线程安全问题
/**
*例子常见三个窗口卖票,总票数为100,使用Runnable接口
* 1.问题:卖票过程中,出现了重票,错票,-->出现了线程安全问题
* 2.问题出现的原因:当某个线程操作车票的过程当中,尚未完成时,其他线程参与进来,也操作了车票。
* 3.如何解决:当一个线程A操作ticket的时候,其他线程不能参与进来,直到线程a操作完ticket的时候,
* 其他线程才可以开始操作ticket,即使线程A出现了阻塞,也不能被改变。
* 4.在Java中,我们通过同步机制,来解决线程的安全问题
* 方式一:同步代码块
* synchronized(同步监视器){
* //需要被同步的代码
* }
* 说明 :1.操作共享数据的代码,即为需要同步的代码
* 2.共享数据:多个线程共同操作的数据,比如本问题中的ticket
* 3.同步监视器:俗称:锁。任何一个类的对象,都可以充当锁。
* 要求:多个线程必须要共用通一把锁。
* 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this来充当同步监视器。
* 在继承Thread类创建多线程的方式中,慎用this来充当同步监视器,可以考虑使用当前类来使用同步监视器
* 方式二:同步方法
* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法生命为同步的。
* 总结:1.同步方法仍然涉及同步监视器,只是不需要我们显示声明。
* 2.非静态的同步方法中,同步监视器是:this
* 静态的同步方法,同步监视器是:当前类本身。 因为静态方法中不能使用this
*
*
*
* 5.同步的方式,解决了线程安全问题。 ---好处
* 操作同步代码时,只能有一个线程参与,其他想等待。相当于是一个单线程的过程,效率低。 ---局限性
*/
使用同步代码块解决线程安全问题
class window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+":卖出的第"+ticket);
ticket--;
}else {
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
window1 window1 = new window1();
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
Thread thread3 = new Thread(window1);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
把懒汉式修改为线程安全的
/**
* 使用线程全机制,将线程安全的懒汉式,改写为线程安全的
*/
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
if (instance==null){
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
死锁
/**
* 线程死锁问题
* 1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
* 都在等待对方放弃自己需要的同步资源,就形成了死锁。
*
* 2.说明:
* 1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
* 2)我们使用同步时,要避免死锁
*
*
* @author ruirui
* @date 2020/7/30 - 14:13
*/
//代码解释,第一个线程先用a后用B,第二个线程先用B后用A。再加上睡眠就容易出现死锁的状况。
public class ThreadDeadTest {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("1");
s2.append("2");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("3");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("1");
s2.append("2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("3");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
Lock锁
/**
* 解决线程安全问题的方式三:Lock ---JDK5.0新增
* 1.面试题:synchronized 与 Lock 的异同?
* 相同:二者都可以解决线程安全问题
* 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
* Lock需要手动的启动同步lock(),同步结束后也要手动的实心unlock()
* 2.面试题 如何解决线程安全问题?有几种方式。
* 三种:同步代码块,同步方法,lock锁
*
* @author ruirui
* @date 2020/7/30 - 15:23
*/
class window2 implements Runnable{
private int ticket = 100;
//实例化一个ReentrantLock
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+": 售票,票号为:"+ticket);
ticket--;
}else{ break;}
lock.unlock();
}
}
}
public class LockTest {
public static void main(String[] args) {
window2 w2 = new window2();
Thread t1 = new Thread(w2);
Thread t2 = new Thread(w2);
Thread t3 = new Thread(w2);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
线程通信
/**
* 线程通信的例子:使两个线程打印1-100。线程1,线程2,交替打印。
* 涉及到的三个方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
* notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果有多个线程被wait(),就会唤醒优先级最高的那个
* notifyAll():一旦执行此方法,就会唤醒所有被wait()的一个线程
*说明:
* 1.wait(),notify(),notifyAll():三个方法,必须使用在同步代码块或同步方法中。
* 2.wait(),notify(),notifyAll()三个方法的调用者,必须是同步代码块或同步方法中的同步监视器。
* 否则会出现异常。
* 3.wait(),notify(),notifyAll()三个方法是定义在,Object类中的。
*
* @author ruirui
* @date 2020/7/30 - 16:09
*/
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
class Number implements Runnable{
private int num = 1;
@Override
public void run() {
while (true){
synchronized (this) {
notify();
if (num<=100){
System.out.println(Thread.currentThread().getName()+":"+num);
num++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else break;
}
}
}
}
面试题:Sleep方法和Wait方法的异同
相同点: 一旦执行,都可以使当前线程进入阻塞状态。
不同点:1)两个方法声明的位置不同。Thread类中声明的sleep()方法,Object类中声明的wait()方法
2)调用的范围不同:sleep()可以在任何需要的场景下调用。wait()必须在同步代码块或同步方法中。
3)关于是否释放同步监视器的问题:如果两个方法都是用在同步代码块或同步方法中,sleep()不会释放 同步监视器,wait()会释放同步监视器。
经典问题:生产者消费者问题
/**线程通信的应用:经典例题:生产者/消费者问题
*
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
* 店员一次只能持有固定数量的产品(比如:20),如果生产者试图产生更多的产品,店员会
* 叫生产者听一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会
* 告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
*
* 分析:
* 1.是否是多线程问题?是,生产者线程,消费者线程
* 2.是否有共享数据的问题?是,店员(产品)
* 3.如何解决线程安全问题?同步机制,有三种方法
* 4.是否涉及线程的通信?是
*
*
* @author ruirui
* @date 2020/7/30 - 16:37
*/
class Clerk{
private int productCount = 0;
// 生产产品
public synchronized void produceProduct() {
if (productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
notify();//只要生产者生产了一个产品了,消费者才能消费
}else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productCount>0){
System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
productCount--;
notify();//消费者消费产品了,生产者才能继续生产
}else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producter extends Thread{//生产者
private Clerk clerk;
public Producter(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始生产产品……");
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始消费产品……");
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producter p1 = new Producter(clerk);
p1.setName("生产者1");
Consumer c1 = new Consumer(clerk);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
实现Callable接口实现多线程
/**
* 创建线程的方式三:实现Callable接口。 ---JDK5.0新增
*
* 如何理解实现Callable接口的方式创建多线程比实现Runnable接口更强大?
* 1.call()方法是可以有返回值的
* 2.call()方法可以抛出异常,被外面的操作捕获,获取异常信息
* 3.call()是支持泛型的
*
*/
//1.创建一个实现Callable接口的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程执行的操作声明在call()方法中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <=100 ; i++) {
if (i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象,传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);//FutureTask实现了runnable
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中的call()方法的返回值
//get()方法的返回值即为FutureTask构造器参数Callable实现类重写的call()方法的返回值
Object o = futureTask.get();
System.out.println("总和为"+o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程池
/**
* 创建线程的方式四:使用线程池
* 好处:
* 1.提高响应速度(减少创建新线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 3.便于线程管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAilveTime:线程没有任务时,最多保持多长时间后终止
*/
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 1; i <100 ; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 1; i <100 ; i++) {
if (i%2!=0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//设置线程池的属性
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口的实现类的对象
service.execute(new NumberThread());//适用于Runnable
service.execute(new NumberThread1());
// service.submit(Callbele callable);适合适用于Callable
//关闭连接池
service.shutdown();
}
}
注解
/**
* 理解Annotation:
* jdk 5.0新增
*1.作用在代码的注解是:
* @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
* @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
* @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
*
* 2.如何自定义注解:参照@SuppressWarnings定义
* ①注解生命为:@interface
* ②内部定义成员,通常使用value表示
* ③可以指定成员的默认值,使用默认default定义
* ④如果自定义注解没有成员,表明是一个标识作用。
*
* 如果注解有成员,在使用注解时,需要指明成员的值。
* 自定义注解必须配上注解的信息处理流程(通过反射)才有意义
* 自定义注解通常都会指明两个元注解:Retention,Target
*
* 3.jdk的4种元注解:对现有的注解进行解释说明的注解
* @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。SOURCE\CLASS(默认行为)\RUNTIME,只有声明为RUNTIME声明周期的注解,
* 才能通过反射获取。
* @Target - 标记这个注解应该修饰哪种 Java 成员。
* ********************出现频率较低*************************
* @Documented - 标记这些注解是否包含在javadoc用户文档中.
* @Inherited - 被它修饰的Annotaton将具有继承性(默认 注解并没有继承于任何子类)
*
* 4.通过反射获取注解信息,
*
* 6.jdk 8 中注解新特性:可重复注解,类型注解
* 6.1 可重复注解:①在MyAnnotation上声明@Repeatable(),成员值为MyAnnotations.class
* ②MyAnnotation的Target和Retention和MyAnnotations的相同。
*
* 6.2 类型注解:
* ElementType.TYPE_PARAMETER 表示改注解能写在类型变量的声明语句中(如:泛型声明).class Generic<@MyAnnotation T>{} 在@MyAnnotation注解的@target中加入这个类型就可以使用
* ElementType.TYPE_USE 表示改注解能写在使用类型的任何语句中。
*
*
*/
@SuppressWarnings("hello")
public class AnnotationTest {
}
class Generic<@MyAnnotation T>{
}
//注解类
@Target({ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
反射
谈谈你对Class类的理解:Class实例对应着加载到内存的一个运行时类。
没明白的问题:类的加载器
获取Class类的实例
/**
* Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期javac,借助Reflection API获得任何类的内部信息,并能直接操作,任意对象的内部属性和方法。
*/
public class ReflectTest {
//反射之前对于person类的操作
@Test
public void test1(){
//1.创建Persion类的对象
Person person =new Person("梁瑞",21);
//2.通过对象调用器内部的属性和方法
person.age = 10;
System.out.println(person.toString());
//在Person类外部,不可以通过Persion类的对象调用其内部私有结构。
//比如私有的内部属性,私有方法,私有构造器
}
//反射之后,对Persion的操作
@Test
public void test2() throws Exception {
//1.通过反射,创建Person类对象
Class clazz = Person.class;
Constructor con = clazz.getConstructor(String.class, int.class);
Object tom = con.newInstance("Tom", 12);
System.out.println(tom.toString());
//2.通过反射,调用对象指定的属性,方法。
Field age = clazz.getDeclaredField("age");
age.set(tom,10);
System.out.println(tom.toString());
//3.调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(tom);
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性。
Constructor con1 = clazz.getDeclaredConstructor(String.class);
con1.setAccessible(true);
Object p1 = con1.newInstance("ruirui");
System.out.println(p1);
//调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Hanmeimei");
System.out.println(p1);
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
Object o = showNation.invoke(p1, "中国");
System.out.println(o);
}
/*
关于java.lang.Class类的理解
1.类的加载过程:
程序在经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件
加载到内存中。此过程就成为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,
就作为Class的一个实例。
2.换句话说,Class的实例就是一个运行时类 .
3.加载到内存中的内存时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
来获取此运行时类。
*/
//获取Class的实例方式(前三种方式需要掌握)
@Test
public void test3() throws ClassNotFoundException {
//方式一、调用运行类的 .class
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
//方式二、通过运行时类的对象,调用.getClass()
Person p1 = new Person();
Class clazz = p1.getClass(); //getClass方法是在Object中声明的
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.reflect.Person");
System.out.println(clazz==clazz1);
System.out.println(clazz==clazz3);
//方式四:使用类的加载器 ClassLoader(了解)
ClassLoader classLoader = ReflectTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.reflect.Person");
System.out.println(clazz4);
}
}
Person类
public class Person {
private String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void show(){
System.out.println("你好,我是一个人");
}
private String showNation(String nation){
System.out.println("我的国籍是: "+nation);
return nation;
}
}
类的加载器
/*
了解类的加载器:1.引导类加载器:它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
2.扩展类加载器:它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3.系统类加载器:它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
4.除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
*/
public class ClassLoaderTest {
@Test
public void test1(){
//对于已定义类,使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
//调用扩展类加载器的getParent():无法获取引导类类加载器
//引导类加载器主要负责加载Java的核心类库,无法加载自定义类
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
}
/*
Properties:用来读取配置文件.
*/
@Test
public void test2() throws IOException {
Properties pro = new Properties();
//读取配置文件的方式一
// FileInputStream fis= new FileInputStream("F:\\kuangshencode\\JavaSE\\基础语法\\src\\com\\reflect\\jdbc.properties");
// pro.load(fis);
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("com\\reflect\\jdbc.properties");//默认路径是在src下
pro.load(is);
String user = pro.getProperty("user");
String password = pro.getProperty("password");
System.out.println(user+" "+password);
}
}
创建运行时类的对象(重要)
/**
* 通过反射常见对应的运行时类的对象。
*/
public class NewInstanceTest {
@Test
public void test1() throws IllegalAccessException, InstantiationException {
Class<Person> clazz = Person.class;
/*
newInstance():调用此方法,创建对用的运行时类的对象。内部调用了运行时类的空参构造器。
要想此方法正常创建运行时类的对象,要求:
1.运行时类必须提供空参构造器
2.空参构造器的访问权限得够,通常是public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象
2.便于子类继承运行时类时,默认调用super()时,保证父类有此构造器。
*/
Person person = clazz.newInstance();
System.out.println(person);
}
//反射的动态性
@Test
public void test2(){
int i = new Random().nextInt(3);//0,1,2
String classPath = "";
switch (i){
case 0:
classPath = "java.lang.String";
break;
case 1:
classPath = "java.util.Date";//java.sql.Date 没有空参构造器,所以会报错
break;
case 2:
classPath = "com.reflect.Person";
break;
}
try {
Object instance = getInstance(classPath);
System.out.println(instance);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
创建一个指定类的对象
classPath:指定类的全类名
*/
public Object getInstance(String classPath) throws Exception {
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
@Test
public void test3() throws Exception {
//通过newInstance每次获取的对象地址值都不一样,因为它是调用空参构造函数,相当于new了一个对象
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
}
}
获取运行时类的完整结构
获取属性及其内部结构
@Test
public void test1(){
Class<Person> clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
//getDecaredField():获取当前运行时类中的所有属性,不包含父类中的所有属性.
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
//权限修饰符 数据类型 变量名 = ..
@Test
public void test2(){
Class<Person> clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
//1.获取权限修饰符
int modifiers = declaredField.getModifiers();
System.out.println();
//2.数据类型
Class type = declaredField.getType();
System.out.println(type+" "+Modifier.toString(modifiers));
//3.变量名
String name = declaredField.getName();
System.out.println(name);
}
}
获取运行时类的方法及其内部结构
/**
* 获取运行时类的方法结构
*/
public class MethodTest {
@Test
public void test1(){
Class<Person> clazz = Person.class;
//getMethods():获取当前运行时类及其父类中生命为public权限的方法.
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
//getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类中的方法)
Method[] methods1 = clazz.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method);
}
}
/*
@Xxx
权限修修饰符 返回值类型 方法名(参数类型1 形参名1 。。。) throws xxxExceotion
*/
@Test
public void test2(){
Class<Person> calzz = Person.class;
Method[] m = calzz.getDeclaredMethods();
for (Method method : m) {
//1.获取方法声明的注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//2.权限修饰符
System.out.print(Modifier.toString(method.getModifiers())+"\t");
//3.返回值类型
Class returnType = method.getReturnType();
System.out.print(returnType.getName()+"\t");
//4.方法名
System.out.print(method.getName());
System.out.print("(");
//5.形参列表
Class<?>[] types = method.getParameterTypes();
if (!(types==null&&types.length==0)){
for (int i = 0; i <types.length ; i++) {
if (i==types.length-1) {
System.out.print(types[i].getName()+"args_"+i);
break;
}
System.out.print(types[i].getName()+" args_"+i+",");
}
}
System.out.print(")");
//6.抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length>0){
System.out.print("throws");
for (int i = 0; i <exceptionTypes.length ; i++) {
if(i==exceptionTypes.length-1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName()+",");
}
}
System.out.println();
}
}
}
获取构造器结构
/*
获取构造器结构
*/
@Test
public void test1(){
Class<Person> clazz = Person.class;
//获取当前运行时类中,声明为public的构造器
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
获取运行时类的其他结构(接口,父类)
/*
获取构造器结构
*/
@Test
public void test1(){
Class<Person> clazz = Person.class;
//获取当前运行时类中,声明为public的构造器
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
@Test
public void test2(){
//获取运行时类的父类
Class<Person> clazz = Person.class;
Class<? super Person> superclass = clazz.getSuperclass();
System.out.println(superclass);
}
@Test
public void test3(){
//获取运行时类带泛型的父类
Class<Person> clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
@Test
public void test4(){
//获取运行时类带泛型父类的泛型
Class<Person> clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType type = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = type.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
@Test
public void test5(){
//获取运行时类实现的接口
Class<Person> clazz = Person.class;
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
}
@Test
public void test6(){
//获取运行时类所在的包
Class<Person> clazz = Person.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
}
@Test
public void test7(){
Class<Person> clazz = Person.class;
//获取运行时类声明的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
调用运行时类的特定结构
/**
* 调用运行时类的指定结构:属性,方法,构造器
*/
public class ReflectionTest {
/*
*/
@Test
public void test1() throws Exception {
Class<Person> clazz = Person.class;
//创建运行时类的对象
Person person = clazz.newInstance();
//获取指定属性:要求运行时类的属性为public的
//通常不采用此方法
Field id = clazz.getField("id");
//设置当前属性的值 set():参数1 设置指明那个对象的属性 参数2:将属性设置为多少
id.set(person,1001);
/*
获取当前属性值
get():参数1:获取哪个对象的当前属性
*/
int pid = (int)id.get(person);
System.out.println(pid);
}
//如何操作运行时类指定的属性
@Test
public void test2() throws Exception {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
//getDeclaredField(String fieldName):获取运行时类的指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取,设置指定对象的此属性值
name.set(person,"Tom");
System.out.println(name.get(person));
}
/*
如何操作指定的运行时类的方法 -- 需要掌握
*/
@Test
public void testMethod() throws Exception {
Class<Person> clazz = Person.class;
System.out.println(clazz);
//创建运行时类的对象
Person person = clazz.newInstance();
/*1.获取指定的方法
getDeclaredMethod():参数1 :指明获取方法的名称 参数 2:指明获取方法的形参列表。
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show.setAccessible(true);
//调用invoke():参数1:方法的调用者 参数2:给形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值。
Object result = show.invoke(person, "CHN");
System.out.println(result);
System.out.println("*******************如何调用静态方法***********************");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用运行时类中的方法没有返回值,则此invoke()返回null
showDesc.invoke(Person.class);
}
/*
如何调用运行时类指定的构造器
*/
@Test
public void twst3() throws Exception {
Class<Person> clazz = Person.class;
/*
1.获取指定的构造器
getDeclaredConstructor():参数,指明构造器的参数列表
*/
Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
declaredConstructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person tom = declaredConstructor.newInstance("Tom");
System.out.println(tom);
}
}
代理模式
静态代理:代理类和被代理类在编译期间就被确定下来,不利于程序的扩展,每一个代理类只为一个接口服务,这样一来程序开发中必然产生过多的代理。
/*
接口的应用:代理模式
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
//
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
动态代理:
/*
动态代理举例
*/
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance:代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法。
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("麻辣烫");
System.out.println("******************************************");
NikeClothFactory nike = new NikeClothFactory();
ClothFactory proxyInstance1 = (ClothFactory)ProxyFactory.getProxyInstance(nike);
proxyInstance1.produceCloth();
}
}
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
public String getBelief() {
return "I BELIEVE I CAN FLY!";
}
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
要想生成动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
*/
class ProxyFactory{
//调用此方法创建一个代理累的对象
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler invocationHandler = new MyInvocationHandler();
invocationHandler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),invocationHandler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj=obj;
}
//当我们通过代理类的对象,调用方法A时,就会调用如下方法:invoke()
//将被代理类执行的方法A的功能就声明在invoke()中
//参数1:代理类的对象 参数2:代理类的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类调用的方法
Object retureValue = method.invoke(obj, args);
//上述方法的返回值就作为invoke()方法的返回值。
return retureValue;
}
}
使用动态代理理解AOP
/*
动态代理举例
*/
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance:代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法。
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("麻辣烫");
System.out.println("******************************************");
NikeClothFactory nike = new NikeClothFactory();
ClothFactory proxyInstance1 = (ClothFactory)ProxyFactory.getProxyInstance(nike);
proxyInstance1.produceCloth();
}
}
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
public String getBelief() {
return "I BELIEVE I CAN FLY!";
}
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
要想生成动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
*/
class ProxyFactory{
//调用此方法创建一个代理累的对象
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler invocationHandler = new MyInvocationHandler();
invocationHandler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),invocationHandler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj=obj;
}
//当我们通过代理类的对象,调用方法A时,就会调用如下方法:invoke()
//将被代理类执行的方法A的功能就声明在invoke()中
//参数1:代理类的对象 参数2:代理类的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil humanUtil = new HumanUtil();
humanUtil.method1();
//method:即为代理类对象调用的方法,此方法也就作为了被代理类调用的方法
Object retureValue = method.invoke(obj, args);
humanUtil.method2();
//上述方法的返回值就作为invoke()方法的返回值。
return retureValue;
}
}
class HumanUtil{
public void method1(){
System.out.println("===============通用方法一=================");
}
public void method2(){
System.out.println("==================通用方法二======================");
}
}