Java高级
1、输入输出相关(I/O相关)
- 流:数据流,在java.io包下
- 流的分类:文件流,字符流,数据流,对象流,网络流等
- 文件:一种电脑的存储形式,
txt,jpg,doc,ppt,mp4,rar,xls,mp3 - 文件夹:目录(路径),File-->与电脑上的文件或文件夹产生对应的映射关系
- File类:File是在内存中的一个对象,与硬盘上的文件和文件夹有映射关系
import java.io;
public class Test {
public static void main(String[] args){
//file是文件的映射,是堆内存中创建出来的一个对象空间
File file = new File(pathname);
//路径是看创建对象,是否能与硬盘上文件创建映射关系
//通过文件流去读取文件的内容
//硬盘上文件名字不区分大小写,内存中File对象,变量名区分大小写
//文件的一些属性:
boolean r = file.canRead();//是否可读
boolean w = file.canWrite();//是否可写
boolean h = file.isHidden();//文件是否隐藏
boolean f = file.isFile();//判断是否是一个文件
boolean d = file.isDirectory();//判断是否是一个文件夹
boolean e = file.isExcute();//是否可执行
long size = file.length();//查看文件大小,返回字节数
Date modifiedDate = file.lastModified();//文件最后修改时间
Date date = file.setLastModified(time);//修改文件时间
//以下方法常用
String a_path = file.getAbsolutePath();//获取文件的绝对路径
String name = file.getName();//获取文件的名字
String r_path = //相对路径,没有盘符的写法,去当前工程所在文件夹中寻找,在src文件夹中寻找
boolean file.crateNewFile();//创建新的文件
boolean file.mkdir();//创建文件夹
boolean file.mkdirs();//创建文件夹,外层没有会创建新的文件夹
String getParent();//获取当前file的父亲file的名字
File getParentFile();//获取当前的file的父亲的file对象
String[] list();//获取当前file的所有儿子的名字
File[] listFiles();//获取所有儿子对象
}
}
- 使用示例:
File file = new File("pathname");
boolean bool = file.mkdir();//前提是外层(父元素)真实存在
try {
boolean value = file.createNewFile();
}catch(IOException e){
e.printStackTrace();
}
//如果file.list()不为空,说明file是个文件夹,为空说明file是个文件
- 文件夹的删除:
public class Test {
public static void main (String[] args){
Test t = new Test();
File f = new File("D://myFolder");
t.deleteFiles(t);
}
//使用递归方式删除所有文件
public void deleteFiles (File file){
File[] files = file.listFiles();
//如果files是个文件则为空,为空文件夹length为0
if(files != null && files.length != 0){
for(File f:files){
deleteFiles(f);
}
}
//删除文件
file.delete();
}
}
-
字节型文件流:
为什么将数据存储在文件中:变量,数组,对象都是存储在内存中,程序执行完毕后,空间会回收
文件存储在硬盘上是永久性保存的,文件不在内存中,需要通过IO进行更改
字节型文件输入流:
//字节型文件流(1字节)FileInputStream/FileOutputStream
//字符型文件流(2字节)FileReader/FileWriter
//JDBC:java database connection
//字节型文件输入流,java.io包 继承InputStream类
//构造方法传参:String,File
try {
File file = new File("D://myFolder/yyy/1.txt");
FileInputStream files = new FileInputStream(file);
int i = files.read();//读取一个字节,读不到返回值-1
//一次读取一个
while(i!=-1){
System.out.print((char)i);
i = files.read();
}
}catch(IOException e){
System.out.println("catch an error.");
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}}
//改进让读取更快
/*txt文件:
hello world,(默认有\t制表符)
nihao
China
*/
//健壮性的写法
FileInputStream files = null;
try {
File file = new File("D://myFolders/yyy/1.txt");
//文件不允许不存在
files = new FileInputStream(file);
byte[] b = new byte[5];
int i = files.read(b);
while(i!=-1){
System.out.print(new String(b,0,i));
i = files.read(b);
/*输出:
hello world,
nihao
ChinaChin
原因:
1:hello
2:\sworl
3:d,\n\tn
4:ihao\n
5:\tChin
6:aChin
*/
}catch(Exception e){
e.printStackTrace();
}finally {
try{
if(files!=null){
files.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
FileInputStream的方法:1、
int available();2、
int code = read();每次从流管道中读取一个字节,返回字节的code码3、
int count = read(byte[] b);每次从管道中读取个字节存入数组,返回有效元素的个数4、
long skip(long n);跳过n个字节往下读;使用场景,多线程,线程1读取1-2000,线程2读取2001-4000,...然后最后将文件拼接成完整文件5、
close()必须执行,无参数无返回值,关闭流管道,建议将关闭操作放在finally里我们打开的文件,对文件进行删除操作时允许的,必须关闭流管道后才能删除
文件流的原理图:
(1)字节型文件输出流
FileOutputStream:讲数据写入文件中java.io包下,父类是OutputStream
public class Test {
public static void main (String[] args){
try{
File file = new File("D://myFolder/yyy/1.txt");
//不管文件是否存在,输出流会创建一个文件
FileOutputStream ofile = new FileOutputStream(file,true);//布尔值表示是否追加,否则每次创建文件覆盖
ofile.write(97);//写入字符
ofile.flush();//将管道中字符推到文件中去
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(ofile!=null){
ofile.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
| InputStream | OutputStream |
|---|---|
| new FileInputStream(file) | new FileOutputStream(file,true) |
| new FileInputStream("path") | new FileOutputStream("path",true) |
| int read() | write(int code) |
| int read(byte[]) | write(byte[],0,count) |
| close() | close() |
| available()查看管道中有多少字节缓存 | flush()将管道中字节型文件流推入文件 |
| skip(long count)跳过多少字节进行读取 |
实现文件复制代码:
public class OperateFile {
public void copy(File file,String path){
try {
//获取要拷贝的对象
FileInputStream ifile = new FileInputStream(file);
byte[] b = new byte[1024];//1kb-8kb比较好
int count = ifile.read(b);
//写入的文件
File tfile = new File(path+"/"+file.getName());
FileOutputStream ofile = new FileOutputStream(tfile);
while(count!=null){
ofile.write(b,0,count);
ofile.flush();
count = ifile.read(b);
}
}catch (IOException e){
e.printStackTrace();
}finally{
//两个文件分别单独判断然后关闭
//分开判断是为了防止意外前面的报错,后面的管道也无法关闭
}
}
}
文件夹的复制:
public void copyFile (File file,String path){
//判断file是文件还是文件夹
File files = file.listFiles();
String oldPath = file.getAbsolutePath();
String newPath = path.concat(oldPath.split(":")[1]);
File newFile = new File(newPath);
if(files != null){
//创建新的file对象
newFile.mkdir();
if(files.length!=0){
for(File f:files){
//此时的新路径就是要拷贝的路径
copyFile(f,newPath);
}
}
}else{
//对文件流的操作
FileInputStream inFile = null;
FileOutputStream outFile = null;
try{
inFile = new FileInputStream(oldPath);
outFile = new FileOutputStream(newPath);
byte[] b = new byte[1024];
int count = inFile.read(b);
while(count!=-1){
outFile.write(b,0,count);
outFile.flush();
count = inFile.read(b);
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(inFile!=null){
inFile.close();
}catch(IOException e){
e.printStackTrace();
}
try{
if(outFile!=null){
inFile.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
剪切操作:
public void cut (File file,String path){
this.copy(file,path);
this.delete(file);
}
(2)字符型文件流
字节流的好处在于可以处理任意格式文件,但是处理中文的时候可能出现字符拼接问题
原因:因为中文由两个字节组成,拼接的时候容易与其它字节拼错,出现乱码
字符型文件输出流的类:FileReader(),FileWriter()
1)在java.io包下,构造方法可以传文件路径或者传文件
2)特点:继承OutputStreamWriter可以读取char[]数组
3)使用:只针对与能用记事本打开的纯文本文件.txt .properties
4)FileWriter(file,true);true表示文件形式为追加,否则均为覆盖
public class Test {
public static void main (String[] args){
try{
File file = new File("D:\\myFolder\\yyy\\y2.txt");
FileReader fd = new FileReader(file);
char[] c = new char[20];
int count = fd.read(c);
while(count!=-1){
//一般编码使用GBK
System.out.print(new String(c,0,count));
count = fd.read(c);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
FileWriter:
与FilerReader基本一样,不同的是write方法:
write(int);
write(char[]);
write(String str);//可以写字符串,较为方便
//注意使用close方法关闭
(3)编码字符集的问题:
eclipse默认是GBK的编码字符集
IDEA默认是UTF-8字符集,将记事本的格式改为utf-8就可以
字符:文字和符号的总称;不同国家的数字、符号、字母是一样的
字符编码:将字符进行拆分和组合的规则
编码集:
1)ASCII(American Standard Code for Information Interchange)只支持英文
2)ISO-8859-1不支持中文
3)GB2312 GB18030 GBK BIG5,Windows平台默认字符集GBK,Linux和Mac OS默认字符集是utf-8
4)Unicode
5)UTF-8
6)UTF-16
(4)缓冲流
在流管道中增加缓存的数据,让我们使用流读取的文字更加顺畅,高级流,通过低级流创建,性能更高。
类:BufferedInputStream/BufferedOutputStream/BufferedReader/Bufferedwriter
BufferedInputStream/BufferedOutputStream:
public class Test {
public static void main (String[] args){
try{
File file = new File("D://myFolder/xxx/x1.txt");
FileInputStream inFile = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(inFile);
//使用方法与低级流相似
bis.read(int/byte);
bis.isAvailable();//查看缓存中字符数
bis.skip(long);
bis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
BufferedReader/BufferedWriter:
//创建字符输入流
File file = new File(path);
FileReader fr = new FileReader(file);
BufferedFileReader bfr = new BufferedFileReader(fr);
int code = bfr.read();
//使用方式与其他流差不多,但是有一个比较方便的方法
String bfr.readLine();//读取文件中一行的信息
String value = bfr.readLine();
while(value!=null){
System.out.println(value);
value = bfe.readLine();
}
文件流
低级流:字节型(FileInputStream,FileOutputStream),字符型(FileReader,FileWriter)
高级流:字节型(BufferedInputStream,BufferedOutputStream),字符型(BufferedReader,BufferedWriter)
对象流:ObjectInputStream、ObjectOuputStream序列化
(5)对象流
1)对象的序列化和反序列化
序列化:将一个完整的对象拆分成字节碎片记录在文件中(需要实现Serializable序列化接口)
反序列化:将文件记录的对象反过来组合成一个完整的对象(需要提供版本号),目的是JDK版本不同读出的对象不一致,高级版本可以读低级版本的对象,但是低级版本读高级版本的序列化对象是不允许的
2)文件的作用:
将文件永久性保存,使数据持久化
3)按照以行为单位读取/写入信息的优缺点:
好处是每一行记录的信息都是相关的,信息直接读取出来,直接了解文件;缺点是不安全,性能差,采用分布式将一个文件拆分成碎片,只能记录String信息,不能记录一些动作和行为
*4)*对象流可以直接将一个对象的属性和行为写进一个对象
public class Person implements Serializable{
private String name;
private int age;
//IDEA 由快捷键生成这个ID
private long final serialVersionID = 437194743254L;
public Person (String name,int age){
this.name = name;
this.age = age;
}
public void talk (){
System.out.println("hello,I am"+this.name);
}
}
//EOFException:说明没有对象可以写了,通常会将所有记录的对象存在一个集合里
public class Test {
public static void main (String[] args){
try{
Person p = new Person("chen",20);
//高级流,需要低级流
//对象的序列化,必须是先Serializable接口(示意性接口,没有需要重写的方法)
FileOutputStream file = new FileOutputStream("D://1.txt");
ObjectOutputStream os = new ObjectOutputStream(file);
os.writeObject(p);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(os!=null){
os.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
使用inteliJ IDEA如何设置自动生成serialVersionID:
File/Settings/Inspections搜索serialVersion勾选下面两个选项:
Serializable class without "serialVersionUID"SerialVersionUID field not declared "private static final"
示意图:
字符串流
StringReader,StringWriter
数据流
DataInputStream,DataOutputStream
转换流
网络相关介绍
打印流
InputStreamReader,PrintWriter
(6)字节型转化为字符型
没错,就是这个流:InputStreamReader
public class Demo {
public void transferStream () throws Exception {
}
}
小任务:
1.实现一个银行业务系统
2.要求有如下业务功能:登录、查询余额,存款,取款,转账,开户,销户
3.记录银行的用户信息:账号,密码,余额
MVC分层架构思想:
2、线程相关
线程例子:看视频时,边看视频边调节声音大小
程序:可以理解为一组静态的代码(下载的程序的文件夹里的所有文件)
进程:正在运行的程序,静态的代码执行起来了(点击exe打开软件,放到内存中执行)
线程:进程内的小单元(进程中的最小单元,很多线程同时执行)
主线程:系统线程(JVM虚拟机,必须先执行起来,其它代码才能执行起来)
用户线程:main
守护线程(精灵):GC
线程:操作系统级别CPU
如何在Java中创建线程,让线程执行,多线程
线程的状态图:
如何在Java中实现一个线程:
1.自己描述一个类
2.继承一个父类(Thread)
3.重写run方法
4.new一个线程对象,调用start方法,让线程进入就绪状态,等待cpu分配时间碎片
实现一个跑步的小例子:
public class TestThread {
//第一种创建线程方法
Runner r1 = new Runner("jieke");
Runner r2 = new Runner("fake");
Runner r3 = new Runner("quene");
r1.start();
r2.start();
r3.start();
//第二种创建线程的方法:构建成线程对象,然后使用run方法
Thread a1 = new Thread(r1);
a1.start();
Thread a2 = new Thread(r1);
a2.start();
}
//实现线程的第一个方式
public class Runner extends Thread {
private String name;
public Runner (String name){
this.name = name;
}
public run () {
for(int i = 0;i < 100;i++){
System.out.println(this.name+" run "+"meters");
}
}
}
//实现的第二个方式
public class Runner implements Runnable {
private String name;
public Runner (String name){
this.name = name;
}
public run () {
for(int i = 0;i < 100;i++){
System.out.println(this.name+" run "+(i+1)+" meters");
}
}
}
多线程使用:模拟多个用户同时访问
可能产生问题:
生产消费者模型
特征修饰符:synchronized放在方法的结构上,锁定的是调用方法时的对象
访问对象的线程锁定了它,所以也叫线程锁
将synchronized放在方法,构造方法,块的内部
1.第一种;
public synchronized void get (){}
2.第二种:提高性能的;
public void get (){
//可以并发执行的代码;
synchronized(对象){
//不允许线程同步的代码
}
//可以并发执行的代码
}
//线程之间状态的切换
this.wait();//是访问当前对象的线程wait
//设置优先级别1-10
production.setPriority(1);
production.getPriority();//获取线程优先级别
IllegalMonitorStateException:告知一个线程等待,结果告知的是另一个线程,会出这个异常
不加锁利用线程等待可能导致的异常:IllegalMonitorStateException
小任务:
微信抢红包例子:5元钱,10个红包,保证每个人至少有0.01
join方法:
Thread类中的方法,可以让并行的线程变成单线程
public class Thread1 extends Thread {
public void run () {
System.out.println("Thread one starts");
Thread2 t2 = new Thread2();
t2.start();
try{
t2.join(2000);//表示让线程先执行2000ms
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Thread one ends");
}
}
public class Thread2 extends Thread {
public void run () {
System.out.println("Thread two starts");
try{
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Thread two ends");
}
}
public class Test {
public static void main (String[] args){
Thread1 t1 = new Thread1();
t1.start();
/*执行结果:
Thread one starts
Thread two starts
Therad one ends
Thread two ends
*/
}
}
死锁
哲学家就餐问题:一个方桌四个人,四个筷子,每个人先拿左手边筷子,在拿右手边筷子
程序中可能出现死锁问题
public class Chopstick {
private int num;
public Chopstick (int num){
this.num = num;
}
public int getNum (){
return this.num;
}
}
public class Person extends Thread{
public String name;
public Chopstick left;
public Chopstick right;
public Person (String name,Chopstick left,Chopstick right){
this.name=name;
this.left=left;
this.right=right;
}
public void run (){
System.out.println(this.name+"开始吃饭");
synchronized (left){
System.out.println(this.name+"拿起了"+left.getNum()+"号筷子");
synchronized (right){
System.out.println(this.name+"拿起了"+right.getNum()+"号筷子");
System.out.println(this.name+"开始吃饭");
}
}
}
}
死锁的解决方法:时间差,不要产生公用对象的问题
计时器/定时器:
public void test (){
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firstTime = sdf.parse("2019-6-11 15:15:00");
timer.schedule(new TimerTask(){
public void run (){
System.out.println("给XXX发送信息:");
}
},firstTime,3000);
}
进程:当前正在运行的程序,一个应用程序在内存中的执行区域。
线程:进程中的一个执行控制单元,执行路径
进程和线程的关系:一个进程可以有一个线程,也可以有多个线程
CPU执行线程的随机性:每个程序中高速切换
多线程提高效率的原理:比如同时复制多个文件,一个进程有多个线程,随机到这个进程的机率就更大,相对而言,效率会更高
多线程实现1:
//线程的创建
public class MyThread extends Thread {
public void run (){
for(int i = 0; i < 100; i++){
System.out.println(this.getName()+":"+i);
}
}
}
public class TestThread {
public static void main (String[] args){
MyThread mt = new MyThread();
//给线程设置名字
mt.setName("张三");
mt.start();
MyThread mt2 = new MyThread();
mt2.setName("老王");
mt2.start();
}
}
String getName();//获取线程名字
void setName();//设置线程名字
主方法是单线程的
多线程实现2:
//实现Runnable接口
public class MyThread implements Runnable {
public void run (){
for(int i = 0; i < 100; i++){
//链式编程
System.out.println(Thread.getCurrentThread().getName()+":"+i);
}
}
}
public class Test {
public static void main (String[] args){
MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.setName("李四");
t.start();
MyThread mt2 = new MyThread();
Thread t2 = new Thread(mt2);
t2.setName("王五");
t2.start();
}
}
//获取当前线程
static Thread Thread.getCurrentThread();//获取当前线程
思考:既然已经有Thread父类了,为什么还要一个Runnable接口?
答:因为Java是单继承的,如果继承了一个父类,就不能在继承其它类,所以为了能继承其它类,又能实现多线程,Java提供一个Runnable接口,实际中,推荐使用第二种,实现Runnable接口的方式来进行多线程开发。
利用多线程实现火车站购票的Demo:
public class Ticket implements Runnable {
private int num;
public Ticket (int num){
this.num = num;
}
public void run (){
while(true){
if(num>0){
System.out.println(Thread.getCurrentThread().getName()+"卖出"+(this.num--)+"号票";
}else{
System.out.println("票已经售完");
}
}
}
}
class Test {
public static void main (String[] args){
Ticket t = new Ticket(200);
Thread thread1 = new Thread(t);
thread1.setName("window 1");
Thread thread2 = new Thread(t);
thread1.setName("window 2");
Thread thread3 = new Thread(t);
thread1.setName("window 3");
thread1.start();
thread2.start();
thread3.start();
}
}
Thread.sleep();//让程序休眠一会儿
注意:
synchronized:一旦被某个线程访问,其它线程都变成等待状态
1、非静态同步方法锁的对象是this 2、静态的同步的方法锁的对象是当前类的字节码对象
线程的生命周期:
)
3、反射相关
String的内容不是真的不让变,可以通过反射尝试修改value的值
反射:reflect,用来描述所有的类,所有的类也具有相同的特征
反射原理图:
类是用来描述一组对象的,反射机制认为是用来描述一组类的
Class:用来描述类本身
Field:用来描述类的属性
Method:用来描述类中的方法
Constructor:用来描述类中的构造方法
Annotation:用来描述类中的注解
获取类的方式:
Class x = Class.forName("包名.类名");
Class y = 类的实例.class;
对象.getClass();//Object中的方法
常用方法:
1.int getModifiers();//每一个修饰符用一个整数来表示
//0:默认不写 1:public 2:private 4:protected 8:static
//16:final 32:synchronized 64:volatile 128:transsient 256:native
//512:interface 1024:abstract
2.String name = getName();//获取全名,包括包名
3.String simpleName = getSimpleName();//只获取类的名字
4.Package p = getPackage();//然后使用p.getName()获取包的名字
5.Class father = getSuperClass();//获取父类
6.Class[] classes = getInterfaces();//获取类中所有的接口
//获取所有父类的全名
public class Test {
public static void main (String[] args) {
ArrayList<String> list = new ArrayList<>();
Class superClass = ArrayList.class;
while(superClass.getSuperclass()!=null){
try{
System.out.println(superClass.getName()+"的父类是"+superClass.getSuperclass().getName());
superClass = superClass.getSuperclass();
}catch(Exception e){
System.out.println("没有了");
}
}
}
}
//获取所有的接口
public class Test {
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
Class superClass = ArrayList.class;
try {
//获取class的所有父亲接口
Class[] classes = superClass.getInterfaces();
while(classes!=null){
for(Class c:classes){
System.out.println(c);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
7.Object newInstance();//或取无参构造方法的实例
Class clazz = Class.forName("testInterface.Person");
Person p = (Person)clazz.newInstance();//相当于调用person类中的默认无参构造方法
8. Field = getField("属性名");//获取属性,属性必须是公有的
Field[] fields = getFields();//获取类的所有属性
Field getDeclaredField("属性");//获取声明的属性
Field[] getDeclaredFields("属性");//获取所有声明的属性
int getModifiers();
Class getType();//获取属性类型对应的那个class
String getName();
set(Object,"value");//给属性赋值得方法
nameField.get(Object)
10. Class[] getClasses();//获取类中的内部类
体会反射的强大:
public class Test {
public static void main (String[] args) {
try{
Class car = Class.forName("velometer.Car");
Car car1 = (Car)car.newInstance();
Field speed = car.getDeclaredField("speed");
//让私有属性可以修改
speed.setAccessible(true);
//将具体的对象中的属性进行设置
speed.set(car1,1000);
//输出证明私有属性也被我们更改了
System.out.println(car1.getSpeed());//1000
}catch(Exception e){
e.printStackTrace();
}
}
}
//虽然是私有的静态属性,只能在本类中设计方法更改,但是反射可以做到,不能修改final修饰的属性
public class Car {
private static int speed = 50;
private static String name = "Landroover";
public Car () {}
public int getSpeed () {
return this.speed;
}
}
通过反射来让字符串能改变:
String str = new String("abc");
Class clazz = str.getClass();
//根据知道的final修饰的value获取到属性
Field field = clazz.getDeclaredField("value");
//将属性变为可修改类型
field.setAccessible(true);
//获取到数组,然后对内容进行修改
char[] temp = (char[])field.get(str);
观察者设计模式(Observer):也可以称之为发布订阅模式
//实现目标的抽象类
public abstract class Subject {
//设置一个存储观察者的集合
private ArrayList<Observer> oSet = new ArrayList<>();
//设置一个敏感方法,用来触发所有观察者的状态更新
public abstract dosomething ();
//唤醒所有观察者
public abstract notifyObservers();
//添加观察者
public void attach (Observer o){
this.oSet.add(o);
}
//删除观察者
public void detach (int index){
this.oSet.remove(index);
}
}
//实现观察者的接口
public interface Observer {
void doself ();
}
反射的方法使用:
public class Test {
public static void main (String[] args){
Class p = Class.forName("packa.Person");
//通过方法名定位方法,通过方法参数类型对应的Class来找寻
try{
//第二个参数是参数类型的类,可以获取类和父类的方法
Method m = p.getMethod("eat",String.class);
int mm = m.getModifiers();//获取权限和特征修饰符
Class mrt = m.getReturnType();//获取返回值数据类型
String mn = m.getName();//获取方法的名字
Class[] mpts = m.getParameterTypes();//获取方法参数列表的类型
Class[] mets = m.getExceptionTypes();//获取异常类型
String str = (String)m.invoke(p,"测试参数");
Method[] ms = m.getMethods();
//使用私有方法
m.setAccessible("true");
m.invoke(p,参数);
}catch(Exception e){
e.printStackTrace();
}
}
}
public class Person {
public String name;
public void eat (){
System.out.println("hello");
}
public void eat (String food){
System.out.println("hello");
}
}
构造方法:
public class Test {
public static void main (String[] args){
try{
Class clazz = Person.class;
//获取有参数的构造方法
Constructor con = clazz.getConstructor(String.class);
//执行构造方法
Person p = (Person)con.newInstance("haha");
System.out.println(p);
}catch(Exception e){
e.printStackTrace();
}
}
}
//除了没有返回值,其它方法与一般方法相同
public class Person {
private String name;
public Person (){}
public Person(String name){this.name=name;}
}
反射的使用:
做底层的封装,根据一个字符串创建出一个真实的对象
设计一个小工具,替代我们创建对象的过程,
//Spring开源框架思想:IOV(inverse of control控制反转),AOP(面向切面编程)
public class Test {
public static void main (String[] args){
}
}
public class MyObject {
//设计一个方法,帮我们控制对象的创建
public Object getBean(String className){
Object obj = null;
Scanner input = new Scanner(System.in);
try{
Class clazz = Class.forName(className);
obj = clazz.newInstance();
int index = clazz.toString().lastIndexOf(".");
String classType = clazz.toString().substring(index+1);
//判断是否为Chacracter类型
//包装类中Character没有String类型的构造方法
if(classType.equals("Character")){
System.out.println("我也不知道要干什么,反射不熟练");
}else{
Field[] fields = clazz.getDeclaredFields();
for(Field f:fields){
//要得到方法名,最后变为set+Demo类型
System.out.println("请给属性"+f.getName()+"赋值:");
//要进行的赋值操作
String value = input.nextLine();
//获取的属性名变为set形式
String attrName = f.getName();
//通过处理好的set方法名,找寻set方法
String firstUpperLetter = attrName.substring(0,1).toUpperCase();
String otherLetter = attrName.substring(1);
StringBuilder sb = new StringBuilder("set");
sb.append(firstUpperLetter);
sb.append(otherLetter);
//获取field对应的属性所属的类
Class fieldClass = f.getType();
//获取参数的类型,获取属性的包装类
//假设为Integer,则con为Integer的构造方法
Constructor con = fieldClass.getConstructor(String.class);
//调用传入String的构造方法
Method fieldMethod = clazz.getMethod(sb.toString(),fieldClass);
//执行,假如obj为Person类,相当于
//con.newInstance(value)相当于new Integer(value);
//相当于, o.setName(new Integer(value))
fieldMethod.invoke(obj,con.newInstance(value));
}
}
}catch(Exception e){
e.printStackTrace();
}
return obj;
}
}
public class Person {
private String name;
private Integer age;
public Person (){}
public Person (String name,Integer age){
this.name = name;
this.age = age;
}
}
4、注解相关
注释:为了让人们更容易读懂代码,文档注释/** */
注解的写法:@xxx[一些信息]
注解的用法:
1.类的上面(TYPE) 2.属性上面(FIELD) 3.方法上面(METHOD) 4.构造方法上面(CONSTRUCTOR) 5.参数前面
注解的作用:
1.仅用来当作注释 2.检测作用 3.可以携带一些信息
注解中可以携带信息,信息不能随意写,信息类型只能是如下类型:
1.基本数据类型 2.String类型 3.枚举类型enum 4.数组类型 5.注解类型@
import java.lang.annotation.ElementType.*; //描述当前注解可以放在什么位置 @Target({FIELD,METHOD,CONSTRUCTOR}) //通过@interface定义一个新的注解类型,写法与接口类似 public @interface MyAnnotation { //注解方法必须有返回值,返回值类型为规定类型 int test(); //我们用value定义且只有一个方法可以省略方法名 //数组内只有一个元素可以省略大括号 String[] value; } public class Test { //如果方法有多个,方法名和传参都不能省 @MyAnnotation(test=10,value={"ha","he"}) private String name; }2.元注解:用来说明注释
@Target({...})描述当前注解可以出现的地方
@Retention描述注解可以存在的作用域(一般为RUNTIME)
@Inherited描述当前注解是否能被子类继承
@Document描述当前注解是否能被文档所记录
public class Test{
public static void main (String[] args){
Date date = new Date();
date.getYear();//会发现getYear有删除线
/*@Deprecated
*public long getYear(){}
**/
}
//提示
@Override
public void eat (){}
//注释
@Deprecated
public void fuck (){}
//携带信息,文件.propertied .xml 注解
@SuppressWarnings(String[] {"unused","",""})//尽量不用
unused 未被使用
serial 未序列化
rawtypes 集合没有过定义泛型
deprecation 使用废弃的方法
unchecked 出现了泛型问题可以不检测
all 包含以上所有(不推荐使用)
}
如何利用反射技术解析注解
import java.lang.annotation.ElementType.*;
//描述当前注解可以放在什么位置
@Target({FIELD,METHOD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String[] value();
}
public class Person {
@MyAnnotation("chen")
private String name;
}
public class Test {
public static void main (String[] args){
//解析Person类属性上面的注解信息
Class clazz = Person.class;
try{
Field name = clazz.gerDeclaredField("name");
//通过field获取上面的注解对象
MyAnnotation ma = (MyAnnotation)field.getAnnotation(MyAnnotation.class);
//1.正常获取注解的值
String[] values = ma.value();
System.out.println(values[0]);//chen
//2.利用反射执行a中的方法
Annotation ma = field.getAnnotation(MyAnnotation.class)
//获取注解对象的类
Class maclazz = ma.getClass();
Method mamethod = maclazz.getMethod("value");
//主要是注解对象调用方法
String[] values = (String[])mamethod.invoke(ma);
}catch(Exception e){
e.printStackTrace();
}
}
}
注解在开发中应用场景
public class Person {
private String name;
private Integer age;
private String sex;
@MyAnnotation("chen","18","male")
public Person (){}
public String getName() {return this.name;}
public Integer getAge() {return this.age;}
public String getSex() {return this.sex;}
public void setName(String name) {this.name = name;}
public void setAge(Integer age) {this.age= age;}
public void setSex(String sex) {this.sex = sex;}
}
public class MySpring {
Object obj = null;
try{
//1.获取目标对象的类
Class targetClass = Class.forName(className);
//2.创建一个目标对象
Constructor targetCon = targetClass.getConstructor();
obj = targetCon.newInstance();
//3.获取注解中的所有信息
Annotation targetAnno = targetCon.getAnnotation(TargetMsg.class);
Class annoClass = targetAnno.getClass();
Method annoMethod = annoClass.getMethod("value");
String[] values = (String[])annoMethod.invoke(targetAnno);
//4.获取对象的所有属性,遍历
Field[] fields = targetClass.getDeclaredFields();
for(int i = 0;i < values.length; i++){
//5.获取属性名进行拼接,变为set+Name形式
fields[i].setAccessible(true);
String fieldName = fields[i].getName();
StringBuilder sb = new StringBuilder("set");
String front = fieldName.substring(0,1);
String newName = sb.append(front.toUpperCase()).append(fieldName.substring(1)).toString();
//6.获取属性类型对应的String类构造方法
Class fieldType = fields[i].getType();
Constructor fieldCon = fieldType.getConstructor(String.class);
//7.根据拼接获取方法,记得要将方法参数类型放进去
Method setMehtod = targetClass.getDeclaredMethod(newName,fieldType);
//8.调用方法设置属性
setMehtod.invoke(obj,fieldCon.newInstance(values[i]));
}
}catch(Exception e){
e.printStackTrace();
}
return obj;
}
Properties的使用:
在java.util包下
HashTable的子类,是一个map类型的集合
文件的后缀名必须是.properties,文件内容必须是key=value形式;分隔符为=、:或空格
//可以认为是一个高级流
Properties pro = new Properties();
pro.load(new FileReader("src/teststream/Test.properties"));
pro.get(key1);
Enumeration en = pro.propertyNames();
try{
Properties pro = new Properties();
//通过低级流读取文件信息,注意必须是properties格式的文件
pro.load(new FileReader("src/Test.properties"));
//get方法获取键名对应的值
System.out.println((String)pro.get("chenxiang"));
//迭代器
//en有两个方法:hasMoreElements() nextElement(),使用与Iterator极其相似
@SuppressWarnings("unchecked")
Enumeration en = pro.propertyNames();
while(en.hasMoreElements()){
String key = (String)en.nextElement();
String value = (String)pro.getProperty(key);
System.out.println("{"+key+":"+value+"}");
}
}catch(Exception e){
e.printStackTrace();
}