一:线程中的一些方法(线程中存在的现象)
1.1 线程加入
public final void join()
等待该线程中止,其他线程才能继续抢着执行
1.2 线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
作用:让线程间的执行更和谐一些,但是实际上做不到。可以通过后面讲解的知识来实现。
1.3 线程死亡
public final void stop():直接杀死
public void interrupt():直接杀死,在死前,还可以有遗言。
1.4 线程休眠
static void sleep(long millis) 线程睡一会
package com.edu_03;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThread extends Thread{
@Override
public void run() {
//打印一下开始执行的时间
System.out.println("开始时间:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
//休眠10秒钟
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("我被杀死了");
}
System.out.println("结束时间:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
package com.edu_03;
/**
* 1.3 线程死亡
public final void stop():直接杀死
public void interrupt():直接杀死,在死前,还可以有遗言。
*
*/
public class Test {
public static void main(String[] args) {
//创建线程对象
MyThread mt = new MyThread();
//开启线程对象
mt.start();
//在线程处于睡眠的过程中将他杀死
try {
Thread.sleep(3000);
//杀死刚刚开启的线程
//调用stop()方法将线程直接杀死
//mt.stop();//划了一条横线表示该方法已经过时,但是还可以使用
//interrupt():直接杀死,在死前,还可以有遗言。
mt.interrupt();//线程被杀死之后会将后面的代码执行完毕之后,再死去
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
心得:线程礼让不存在,所以了解一下就行了,线程死亡,线程抢占和线程睡眠是开发中用的比较多的,大家可以记一记,这个比较好用,很简单。
二:线程的生命周期(画图讲解),面试题
1.新建
2.就绪
3.运行
4.有可能阻塞
5.死亡
三:线程间通信(生产消费者问题):不同类型线程针对同一个资源的操作
画图讲解:1.系统不仅要卖票还要入票
2.不仅要卖肉夹馍还要生产肉夹馍
四:案例:以给学生设置和获取姓名和年龄为例,演示线程通信问题
线程间通讯:
资源:Student
设置数据线程:SetThread
获取数据线程:GetThread
测试类:StudentDemo
package com.edu_04;
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
//获取线程,获取学生对象的姓名和年龄
//Student s = new Student();
while (true) {
synchronized (s) {
System.out.println(s.name+"--"+s.age);
}
}
}
}
package com.edu_04;
public class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
//给学生对象设置姓名和年龄
//Student s = new Student();
while (true) {
synchronized (s) {
if (x%2==0) {
s.name = "刘嘉玲";
s.age = 50;
}else {
s.name = "陈冠希";
s.age = 35;
}
x++;
}
}
}
}
package com.edu_04;
public class Student {
String name;
int age;
}
package com.edu_04;
/**
* 需求:将学生对象作为一个资源进行操作
* Student -- 被设置的资源对应的类
* setThread -- 设置线程
* getThread -- 获取线程
* StudnetDemo -- 测试类
*
* 问题1:
* 打印null--0
* 原因:设置和获取不是同一个对象
* 请问,怎么解决?
* 当在不同的类中,需要使用同一个数据的时候,可以考虑将数据利用构造方法进行传参
*
* 问题2:
* 2.1:出现重复数据
* 出现
* 陈冠希--35
陈冠希--35
2.2:出现数据错乱,线程安全问题
刘嘉玲--35
陈冠希--50
什么情况下或出现线程安全问题?
a:多线程情况
是
b:存在共享数据
是
c:存在多条语句对共享数据进行操作
是
问题3:
所以我们上面写的所有的代码还是存在线程安全问题的,请问如何解决?
同步代码块,加锁
1.设置线程和获取线程需要全部做一个限制,相当与两个线程搜需要加锁
2.设直线程和获取线程,加的锁必须是同一个
问题4:
我们现在解决了数据的安全问题,但是又产生了一个新的问题,数据一打印就是一大片,
我们现在想让设置和获取的时候这两个线程之间的运行更加的和谐一些。
*
*/
public class StudentDemo {
public static void main(String[] args) {
//创建一个学生对象
Student s = new Student();
//创建设置和获取线程,并开启线程
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//开启线程
t1.start();
t2.start();
}
}
问题1:控制台出现的结果是:null—0
设置和获取线程使用的学生资源不是同一个。
如何解决这个问题呢?
把资源作为构造参数传递即可。
问题2:产生了2个问题
A:相同的数据出现了多次
CPU的一点点时间片就足够我们的程序执行很多次
B:数据出现了问题(数据安全问题)
a:是否是多线程环境
是
b:是否有共享数据
是
c:是否有多条语句操作共享数据
是
既然我们知道它是出现了数据安全问题,我们就应该来解决它。
如何解决呢?加锁
问题3:加了锁以后,数据还是有问题
A:多个线程都要加锁
B:多个线程加的锁必须是同一把锁
五:将上述代码使用等待唤醒机制改进,实现礼让效果
六:将上述代码继续优化
1.私有化Student类的成员变量
2.在类的内部提供设置和获取的同步方法
心得:上面的几个知识点,最终要的是明白那个学生设置姓名年龄和获取姓名年龄的线程间通信问题,这是很重要的,面试会问到,这里要涵盖了线程从新建到死亡的全过程, 问题讲解:这里要注意的几个问题,一:必须是同一把锁进行控制,不然会存在线程安全问题,二:同一个学生对象,使用构造方法传参可解决这个问题,三:就是线程礼让问题了,可采用等待和唤醒机制解决这个问题,在设置一个标记,当在设置线程中时,标记为true,则有数据,陷入等待之中,如果不是,就传入数据,更改标记为true,唤醒线程,在获取线程中,如果标记为false,则无数据,不获取,使线程进入等待之中,否则,打印值,更改标记为false,唤醒线程。 还有就是优化程序,把上述问题封装在student类中,使代码更为简洁。
七:线程组
线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup():获取线程对应的线程组对象
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target)
案例1:创建线程获取对应的线程组对象,并获取名称
案例2:创建线程组对象,给线程分配线程组
package com.edu_07;
/**
* 七:线程组
线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup():获取线程对应的线程组对象
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target)
案例1:创建线程获取对应的线程组对象,并获取名称
案例2:创建线程组对象,给线程分配线程组
*
*/
public class Test {
public static void main(String[] args) {
// 案例1:创建线程获取对应的线程组对象,并获取名称
//创建两个线程
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//获取上面两个线程对应的线程组对象
ThreadGroup tg1 = mt1.getThreadGroup();
ThreadGroup tg2 = mt2.getThreadGroup();
//获取线程组对象的名称
System.out.println(tg1.getName());//main--主线程祖,默认情况下,每一个线程默认都属于主线程祖
System.out.println(tg2.getName());
System.out.println("-----------------");
//案例2:创建线程组对象,给线程分配线程组
//public ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("刘德华");
//public Thread(ThreadGroup group,Runnable target)
Thread t3 = new Thread(tg, new MyRunnable());
Thread t4 = new Thread(tg, new MyRunnable());
//获取t3和t4的线程组对象
ThreadGroup tg3 = t3.getThreadGroup();
ThreadGroup tg4 = t4.getThreadGroup();
System.out.println(tg3.getName());
System.out.println(tg4.getName());
}
}
这个很简单,很容易就掌握了,开发中也会用到
八:线程池
为什么要使用线程池?
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,
尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池的特点:
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
线程池如何创建?
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
线程池的使用步骤:
1.创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
2.创建Runnable实例
MyRunnable my = new MyRunnable();
3.提交Runnable实例
pool.submit(my);
pool.submit(my);
4.关闭线程池
pool.shutdown();
案例1:实现Runnable接口实现线程池的使用
案例2:实现Callable接口实现线程池的使用
案例3:实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和
package com.edu_08;
public class MyRunnbale implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
package com.edu_08;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 八:线程池
为什么要使用线程池?
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,
尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池的特点:
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
线程池如何创建?
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
*
*/
public class ThreadPool {
public static void main(String[] args) {
//线程池如何创建?
//1.调用工厂类Executors
//的public static ExecutorService newFixedThreadPool(int nThreads),返回一个线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//2.提交给线程池两个任务,都是打印0-99
//创建任务
MyRunnbale my1 = new MyRunnbale();
MyRunnbale my2 = new MyRunnbale();
//3.提交任务
pool.submit(my1);
pool.submit(my2);
//关闭线程池
//void shutdown()
pool.shutdown();
}
}
package com.edu_09;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
//案例2:实现Callable接口实现线程池的使用
//1.创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建一个任务
MyCallable my1 = new MyCallable();
MyCallable my2 = new MyCallable();
//3.提交任务
pool.submit(my1);
pool.submit(my2);
//4.关闭线程池
pool.shutdown();
}
}
package com.edu_09;
import java.util.concurrent.Callable;
public class MyCallable implements Callable{
//也是一个任务,只不过这个任务需要执行的方法是call(),这个方法有返回值
@Override
public Object call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
return null;
}
}
心得:重点掌握线程池的开启和使用,感觉他比单线程开启好用多了。还有callable接口的使用,要是要用到返回值得问题,这个比较好使。
九:定时器
Timer
public Timer()构造
public void schedule(TimerTask task, long delay)延迟多久执行任务
public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次
public boolean cancel()取消这个任务
TimerTask
public abstract void run()放的是所要执行的任务代码
案例1:演示以上方法的使用
案例2:定时删除文件
所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
package com.edu_11;
import java.util.Timer;
import java.util.TimerTask;
/**
* 九:定时器
Timer
public Timer()构造
public void schedule(TimerTask task, long delay)延迟多久执行任务
public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次
public boolean cancel()取消这个任务
TimerTask
public abstract void run()放的是所要执行的任务代码
案例1:演示以上方法的使用
案例2:定时删除文件(需要在15:58:00 删除D://a.txt文件)
所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
*
*/
public class TimerTest {
public static void main(String[] args) {
//需求:在10秒钟后,在控制台打印一句话,helloworld
//public Timer()构造
Timer t = new Timer();
//public void schedule(TimerTask task, long delay)延迟多久执行任务
t.schedule(new MyTimerTask(t), 10000);
//public void cancel()终止此计时器
//t.cancel();//如果在这里关闭的话,我们还没等任务执行完毕呢,计时器已经被关闭了
}
}
//创建TimerTask的子类
class MyTimerTask extends TimerTask{
private Timer t;
public MyTimerTask(Timer t){
this.t = t;
}
@Override
public void run() {
//此计时器任务要执行的操作。
System.out.println("helloworld");
t.cancel();//当任务被执行完毕之后,关闭定时器
}
}
package com.edu_11;
import java.util.Timer;
import java.util.TimerTask;
//public void schedule(TimerTask task,long delay,long period)
//延迟多久执行任务,并以后每隔多久执行一次
public class TimerTest2 {
public static void main(String[] args) {
//延迟5秒钟,打印,我爱中国,以后每隔一秒打印一次
Timer t = new Timer();
t.schedule(new MyTimerTask2(), 5000, 1000);
/**
* 参数1:要执行的任务
* 参数2:延迟多久执行
* 参数3:执行一次之后,每隔多久重复执行
*/
}
}
class MyTimerTask2 extends TimerTask{
@Override
public void run() {
System.out.println(“我爱中国”);
}
}
这个在定时删除任务的时候,或者文件的时候,好使,还有就是data对象的创建大家要好好练练,不能捡了芝麻丢了西瓜,这个比喻也不太恰切,就这样。
十:多线程面试题
1.多线程有几种实现方案,分别是哪几种?
两种,继承thread类和实现runnable接口
2.同步有几种方式,分别是什么?
同步代码块 同步方法 同步变量
还有几种我就多说了
3.启动一个线程是run()还是start()?它们的区别?
run()方法是在主线程上运行run方法,
start()方法是先开启一个线程,然后运行run方法
4.sleep()和wait()方法的区别
wait在等待过程中,会释放锁,并必须被唤醒
sleep在等待过程中,不会释放锁,并自行唤醒
5.为什么wait(),notify(),notifyAll()等方法都定义在Object类中
保证线程可以随时等待唤醒
6.线程的生命周期图
1:类加载器(了解)
(1)什么是类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,
初始化三步来实现对这个类进行初始化。
一个类在加载过程中的三部曲:
1.加载
就是指将class文件读入内存,并为之创建一个Class对象.
任何类被使用时系统都会建立一个Class对象。
2.连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
3.初始化 就是我们以前讲过的初始化步骤
(2)类的加载时机(掌握)
2.1 创建类的实例
2.2 访问类的静态变量,或者为静态变量赋值
2.3 调用类的静态方法
2.4 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2.5 初始化某个类的子类
2.6 直接使用java.exe命令来运行某个主类
(3)加载器分类
3.1 类加载起的作用?
负责将.class文件加载到内在中,并为之生成对应的Class对象。
3.2 类加载器的分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件
2:反射(掌握)
Student.java–Student.class(字节码文件)–看成一个对象,这个对象就叫字节码文件对象–对应的类Class
什么是反射?(画图描述)
答:通过字节码文件对象去使用成员。
(1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
案例:使用上述三种方式获取类的Class对象
package com.edu_01;
public class Person {
int age;
public String name;
private String address;
public Person(){}
private Person(String name){
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
protected Person(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
public void show(){
System.out.println("public--show");
}
private String getString(String s1,String s2){
return s1+s2;
}
protected void method(){
System.out.println("protected---method");
}
void function(){
System.out.println("function");
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + ", address=" + address
+ "]";
}
}
package com.edu_01;
/**
* (1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
我们可以把这个参数作为配置文件的内容进行配置,这样就实现了一个变化的内容。
案例:使用上述三种方式获取类的Class对象
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//创建Person对象
Person p1 = new Person();
Person p2 = new Person();
//A:Object类的getClass()方法
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(p1==p2);
System.out.println(c1==c2);
System.out.println("----------------------");
//B:数据类型的静态class属性
Class c3 = Person.class;
System.out.println(c1==c3);
System.out.println("-----------------------");
//C:Class类的静态方法forName()
Class c4 = Class.forName("com.edu_01.Person");
System.out.println(c1==c4);
}
}
(2)反射的使用步骤
Class:
成员变量 Field
构造方法 Constructor
成员方法 Method
反射:
class字节码文件对象 -- 去得到对应的成员对象 -- 通过该成员的对象调用方法使用
通过反射获取构造方法并使用
(3)案例:
1.通过反射获取构造方法
public Constructor[] getConstructors() 获取公共的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
public Constructor getDeclaredConstructor(Class<?>... parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
package com.edu_02;
import java.lang.reflect.Constructor;
public class ConstructorReflectDemo2 {
public static void main(String[] args) throws Exception {
//案例:通过反射获取构造方法并创建对象
//获取类对应的字节码文件对象
Class c = Class.forName("com.edu_01.Person");
/*第一版:
//获取对应的构造方法
Constructor con = c.getConstructor();
//创建对象
Object obj = con.newInstance();*/
/*第二版:
//获取对应的构造方法
Constructor con = c.getDeclaredConstructor(String.class);
//取消访问修饰符检测
con.setAccessible(true);
//创建对象
Object obj = con.newInstance("谢娜");
*/
/*第三版
//获取对应的构造方法
Constructor con = c.getDeclaredConstructor(String.class,int.class);
//取消访问修饰符检测
con.setAccessible(true);
//创建对象
Object obj = con.newInstance("谢娜",30);
*/
//第四版
//获取对应的构造方法
Constructor con = c.getDeclaredConstructor(String.class,int.class,String.class);
//取消访问修饰符检测
con.setAccessible(true);
//创建对象
Object obj = con.newInstance("谢娜",30,"湖南");
//打印对象
System.out.println(obj);
}
}
2.通过反射获取构造方法并创建对象
public T newInstance(Object... initargs)
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
public void set(Object obj,Object value)给一个对象的一个字段设置一个值
package com.edu_03;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
3.通过反射获取成员变量并使用
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
*/
public class FiledReflectDemo {
public static void main(String[] args) throws Exception {
//创建字节码文件对象
//获取类对应的字节码文件对象
Class c = Class.forName("com.edu_01.Person");
//创建一个Person对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
/*第一版:
//获取成员变量的对象
public Field[] getFields()获取公有的成员变量
public Field[] getDeclaredFields()获取全部的成员变量,包括私有
//Field[] fields = c.getFields();//仅能获取公有的成员变量
//Field[] fields = c.getDeclaredFields();//获取全部的成员变量,包括私有
//遍历
for (Field field : fields) {
System.out.println(field);
}*/
/*第二版:
//public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
Field field = c.getField("name");
System.out.println(field);
//给Person对象赋值
field.set(obj, "张杰");
System.out.println(obj);*/
//第三版
//public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
Field fieldName = c.getDeclaredField("name");
Field fieldAge = c.getDeclaredField("age");
Field fieldAddress = c.getDeclaredField("address");
//取消访问权限检测
fieldAddress.setAccessible(true);
fieldAge.setAccessible(true);
//给对象obj赋值
fieldName.set(obj, "张杰");
fieldAge.set(obj, 30);
fieldAddress.set(obj, "江西");
System.out.println(obj);
}
}
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()获取所有成员方法,包括私有
public Method getMethod(String name, Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有
Object invoke(Object obj, Object... args) 让某一个对象使用这个方法,并且传入参数
package com.edu_04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
4.通过反射获取成员方法并使用
public Method[] getMethods()获取所有公共成员方法
public Method[] getDeclaredMethods()获取所有成员方法,包括私有
public Method getMethod(String name, Class<?>... parameterTypes)
参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有
Object invoke(Object obj, Object... args) 让某一个对象使用这个方法,并且传入参数
*/
public class MethodReflectDemo {
public static void main(String[] args) throws Exception{
//创建字节码文件对象
Class<?> c = Class.forName("com.edu_01.Person");
//创建一个Person对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
/* 第一版:
//获取方法
// public Method[] getMethods()获取所有公共成员方法
// public Method[] getDeclaredMethods()获取所有成员方法,包括私有
//Method[] methods = c.getMethods();//不仅获取到自己的还获取到父类的
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}*/
/*第二版:
// public Method getMethod(String name, Class<?>... parameterTypes)
Method m1 = c.getMethod("show");
m1.invoke(obj);
*/
/*第三版:
//public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
Method method = c.getDeclaredMethod("getString", String.class,String.class);
method.setAccessible(true);
Object objString = method.invoke(obj, "张杰","谢娜");
System.out.println(objString);*/
/*第四版:
Method method = c.getDeclaredMethod("method");
method.setAccessible(true);
method.invoke(obj);*/
//第五版
Method method = c.getDeclaredMethod("function");
method.setAccessible(true);
method.invoke(obj);
}
}
(4)面试题:
1:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
package com.edu_06;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayTest {
public static void main(String[] args) throws Exception {
/**
* 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
*
* 泛型来说是给jvm看的,当程序运行起来之后,泛型就会自动去掉,这叫泛型擦除
*/
ArrayList<Integer> arr = new ArrayList<Integer>();
//强行给里面添加字符串会报错
//arr.add("hello");
//通过反射获取add方法的原型,
//获取该类的字节码文件对象
Class c = arr.getClass();
Method methodAdd = c.getDeclaredMethod("add", Object.class);
//通过反射调用add方法
methodAdd.invoke(arr, "hello");
methodAdd.invoke(arr, "world");
methodAdd.invoke(arr, "java");
//打印集合
System.out.println(arr);
}
}