Java java高级篇 Zero02 2025-03-20 2025-03-27 线程 线程概念 单线程:同一个时刻,只允许执行一个线程
多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发
并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行
线程的基本使用 在java中线程使用有两种方法
继承Thread类,重写run方法
注:如果想要让线程持续执行,需要将run方法中的执行流程用while循环包裹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Cat extends Thread { @Override public void run () { while (true ){ System.out.println("喵喵,我是o(=•ェ•=)m" ); try { Thread.sleep(1000 ); }catch (InterruptedException e){ e.printStackTrace(); } } } } Cat cat = new Cat ();cat.start();
实现Runnable接口,重写run方法
Thread实现类Runnable接口,所以也可以让需要多线程的类实现Runnable来重写run方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Dog implements Runnable { int count = 0 ; @Override public void run () { while (true ){ System.out.println("小狗汪汪叫" + (++count) + Thread.currentThread().getName()); try { Thread.sleep(1000 ); }catch (InterruptedException e){ e.printStackTrace(); } if (count == 10 ){ break ; } } } } Dog dog = new Dog ();Thread thread = new Thread (dog);thread.start();
实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议实现Runnable来实现多线程
监控线程和执行情况 使用JConsole可以实现查看线程每时每刻的状态
将程序写好之后,让程序运行。在运行的过程中,打开cmd,输入Jconsole.查找当前运行程序的线程名,就可以查看当前程序运行的情况了。
结论:
当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行
主线程和子线程会交替执行
子线程结束不会影响主线程的执行,当主线程退出时,子线程还会继续执行,并没有完全退出,当所有正在运行的线程结束后,整个进程才会结束
通过start方法才会真正的开启线程,底层会调用start0这个方法。然后调用run方法
1 2 3 4 5 6 7 cat.start(); public synchronized void start () { start0(); } private native void start0 () ;
start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程编程了可运行状态,具体什么时候执行,取决于cpu,有cpu统一调度
线程底层涉及操作系统,有底层系统的函数来实现
线程代理设计模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class ThreadProxy implements Runnable { private Runnable target = null ; @Override public void run () { if (target != null ){ target.run(); } } public ThreadProxy (Runable target) { this .target = target; } public void start () { start0(); } public void start0 () { run(); } } class Animal {}class Tiger extends Animal implements Runnable { @Override public void run () { System.out.println("老虎嗷嗷叫" ); } } Tiger tiger = new Tiger ();new ThreadProxy (tiger).start();
控制线程终止 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class T extends Thread { private int count = 0 ; private boolean l = true ; @Override public void run () { while (l){ try { Thread.sleep(50 ); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("T 运行中..." + (++count)); } } public void setL (boolean l) { this .l = l; } } public static void main () throws InterruptedException{ T t = new T (); t.start(); System.out.println("主线程休眠10秒" ); Thread.sleep(10 *1000 ); t.setL(false ); }
线程常用的方法
setName() 设置线程名称,使之与参数name相同
getName() 返回该线程的名称
start() 使线程开始执行,java虚拟机底层调用该线程的start0方法
run() 调用线程对象的run方法
setPriority() 更改线程的优先级
getPriority() 获取线程的优先级
sleep() 在指定毫秒数内让当前正在执行的线程休眠(暂停执行)
interrupt() 中断线程
start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新的线程
线程优先级的范围:MAX_PRIORITY 10 MIN_PRIORITY 1 NORM_PRIORITY 5
interrupt中断线程,但没有真正的结束线程,所以一般用于中断正在休眠的线程
sleep:线程的静态方法,使当前线程休眠
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class ThreadMethod1 { public static void main (String[] args) throws InterruptedException { T t = new T (); t.setName("老韩" ); t.setPriority(Thread.MIN_PRIORITY); t.start(); for (int i = 0 ; i < 5 ; i++) { Thread.sleep(1000 ); System.out.println("hi" + i); } t.interrupt(); } } class T extends Thread { @Override public void run () { while (true ) { for (int i = 0 ; i < 100 ; i++) { System.out.println(Thread.currentThread().getName() + "吃包子~~~" + i); } try { System.out.println(Thread.currentThread().getName() + "休眠中~~~" ); Thread.sleep(20000 ); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "被interrupt了" ); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ThreadMethod2 { public static void main (String[] args) throws InterruptedException { X x = new X (); x.start(); System.out.println("主线程<小弟>执行" ); for (int i = 1 ; i <= 20 ; i++) { System.out.println("主线程(小弟)吃的包子数量:" + i); Thread.sleep(2000 ); System.out.println("喵喵造~~" ); if (i == 5 ){ System.out.println("主线程(小弟) 让 子线程<老大>先吃" ); Thread.yield (); System.out.println("子线程<老大> 吃完了 主线程(小弟)接着吃" ); } } System.out.println("(小弟吃完了包子)" ); } } class X extends Thread { @Override public void run () { System.out.println("子线程<老大>执行" ); for (int i = 1 ; i <= 20 ; i++) { System.out.println("子线程<老大>吃的包子数量:" + i); try { Thread.sleep(2000 ); System.out.println("喵喵造~~" ); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("<老大>吃完了包子" ); } }
用户线程和守护线程 用户线程:也叫工作线程,当线程的任务执行完成或通知方式结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class MyDaemonThread extends Thread { public void run () { for (;;){ try { Thread.sleep(50 ); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("马蓉和宋喆聊天..." ); } } } public static void main (String[] args) { MyDaemonThread myDaemonThread = new MyDaemonThread (); myDaemonThread.setDaemon(true ); myDaemonThread.start(); for (int i = 1 ;i <= 10 ;i++){ System.out.println("宝强在辛苦的工作..." ); Thread.sleep(1000 ); } }
线程的七大状态 **1.初始(NEW):**刚被创建,还没运行(未执行线程的start()方法)
**2.就绪状态(READY):**线程在可运行线程池 中,但未获得CPU执行权,和RUNNING并称运行
**3.运行中状态(RUNNING):**线程执行并获得CPU执行权,和READY并称运行
**4.阻塞(BLOCK):**等待其他线程释放锁的状态
**5.等待(WAITING):**需要其他线程做出一些约定好的动作,或被唤醒(通知或中断 )
**6.超时等待(TIME_WAITING):**和等待的不同点在于可以在指定的时间自行醒来
**7.终止(TERMENATED):**线程已经执行完毕
解读
NEW 新建状态 尚未启动的线程,使用new
语句创建的线程处于新建状态(new Thread),仅仅在堆上分配了内存
RUNNABLE 运行状态 在Java虚拟机中执行的线程处于此状态
BLOCKED 阻塞状态 当线程由于缺少响应的资源而导致程序无法继续执行,就会从运行状态进入到阻塞状态比 如:锁资源,IO资源
WAITING 等待状态 当线程调用了wait
方法就会进入到WAITING
状态,该状态只有notify
等操作才能唤醒线程进入下一个状态
TIMED_WAITING 睡眠状态 如果线程执行了sleep(long)
/join(long)
/wait(long)
,会触发线程进入到Time_waiting
状态,只有到达设定的时间,才会脱离阻塞状态
TERMINATED 终止状态 已退出的线程处于此状态,该线程结束生命周期
JDK 明明为我们提供了 6 种状态,为什么标题说 7 大状态呢?实际上我们可以将 RUNNABLE 划分为重新两个状态, Ready 和 Running 状态
Ready 就绪状态 当线程创建后,其他线程调用start
方法,该线程就进入到就绪状态,JVM就会为创建方法调用栈和程序计数器,处于这个状态的线程位于可运行的池中,等待获取CPU的使用权 ,其他处于阻塞状态解除阻塞之后也会进入就绪状态
Running 运行状态 处于这个状态的线程占用CPU,执行程序代码,只有处于就绪状态的线程才会有机会转到运行状态
① 一个线程的生命周期状态包括:New 、Runnable 、Running 、Terminated 状态② 线程在需要响应资源时,就会进入到阻塞状态,阻塞状态包含Waiting 、Blocked 、Time_waiting 状态
代码查看线程的所处状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class ThreadStateTest { public static void main (String[] args) throws InterruptedException { T t = new T (); System.out.println(t.getState()); t.start(); while (t.getState() != Thread.State.TERMINATED) { System.out.println(t.getState()); Thread.sleep(500 ); } System.out.println(t.getState()); } } class T extends Thread { @Override public void run () { for (int i = 0 ; i < 5 ; i++) { System.out.println("hi " + (i + 1 )); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程同步 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,知道该线程完成操作,其他线程才能对该内存地址进行操作
当多个线程同时拥有一个数据时,并且同时运行,如果没有线程同步,会产生数据错误的情况,出现过载和超额的可能,这在实际开发中是一个巨大的错误,必须解决。
同步具体方法-Synchronized
同步代码块
1 2 3 4 5 6 7 synchronized(对象){//得到对象的锁,才能操作同步代码 //需要被同步代码; } synchronized还可以放在方法声明中,表示整个方法为同步方法 public synchronized void m(String name){ //需要被同步代码; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class Sell { public static void main (String[] args) { SellTicket2 s1 = new SellTicket2 (); new Thread (s1).start(); new Thread (s1).start(); new Thread (s1).start(); } } class SellTicket2 implements Runnable { private static int ticket = 100 ; private boolean loop = true ; Object object = new Object (); public synchronized static void m1 () {} public static void m2 () { synchronized (SellTicket2.class){ System.out.println("m2" ); } } public void m () { synchronized (object){ if (ticket <= 0 ) { System.out.println("售票结束~~" ); loop = false ; return ; } System.out.println(Thread.currentThread().getName() + "买了一张票数,剩余票数" + (--ticket)); } } @Override public void run () { while (loop) { m(); try { Thread.sleep(200 ); } catch (InterruptedException e) { e.printStackTrace(); } } } } class SellTicket extends Thread { private static int ticket = 100 ; @Override public void run () { do { System.out.println(Thread.currentThread().getName() + "买了一张票数,剩余票数" + (--ticket)); try { Thread.sleep(20 ); } catch (InterruptedException e) { e.printStackTrace(); } } while (ticket >= 0 ); System.out.println("售票结束" ); } }
互斥锁
java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
同步的局限性:导致程序的执行效率要降低
同步方法(非静态的)的锁可以时this,也可以时其他对象(要求是同一个对象)
同步方法(静态的)的锁为当前类本身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public synchronized void sell () { if (ticketNum <= 0 ){ System.out.println("售票结束..." ); loop = false ; return ; } } Object object = new Object ();public void sell () { synchronized (object ){ if (ticketNum <= 0 ){ System.out.println("售票结束" ); loop = false ; return ;l } } } publics synchronized static void m1 () { } public static void m2 () { synchronized (当前类.class){ System.out.println("m2" ); } }
实现的步骤:
需要分析上锁的代码
选择同步代码快 或同步方法
要求多个线程的锁对象为同一个即可
线程死锁 死锁的定义
多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
下面我们通过一些实例来说明死锁现象。
先看生活中的一个实例,两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。
在计算机系统中也存在类似的情况。例如,某计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class DeadLock_ { public static void main (String[] args) { DeadLockDemo A = new DeadLockDemo (true ); A.setName("A线程" ); DeadLockDemo B = new DeadLockDemo (false ); B.setName("B线程" ); A.start(); B.start(); } } class DeadLockDemo extends Thread { static Object o1 = new Object (); static Object o2 = new Object (); boolean flag; public DeadLockDemo (boolean flag) { this .flag = flag; } @Override public void run () { if (flag) { synchronized (o1) { System.out.println(Thread.currentThread().getName() + " 进入1" ); synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入2" ); } } } else { synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入3" ); synchronized (o1) { System.out.println(Thread.currentThread().getName() + " 进入4" ); } } } } }
释放锁 下面的方式会释放锁:
当前线程的同步方法,同步代码块执行结束
当前线程在同步代码块,同步方法中遇到break,return
当前线程在同步代码块,同步方法中痴线了未处理的error或exception,导致异常结束
当前线程在同步艾玛块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
下面的方式不会释放锁:
线程执行同步代码块或者同步方法时,程序会调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
提示:应尽量避免使用suspend() resume()来控制线程,方法不在推荐使用
IO流 创建文件 创建文件对象相关构造器和方法
1 2 3 4 new File(String pathName) 根据路径创建一个File对象 new File(File parent,String child) 根据父目录文件 + 子路径创建 new File(String parent,String child) 根据父目录 + 子路径创建
实现类图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class FileCreate { public static void main (String [] args ) { } @Test public void create01 ( ) { String filePath = "e:\\news1.txt" ; File file = new File (filePath); try { file.createNewFile (); System .out .println ("文件创建成功" ); } catch (IOException e) { e.printStackTrace (); } } @Test public void create02 ( ) { File parentFile = new File ("e:\\" ); String fileName = "news2.txt" ; File file = new File (parentFile, fileName); try { file.createNewFile (); System .out .println ("创建成功~" ); } catch (IOException e) { e.printStackTrace (); } } @Test public void create03 ( ) { String parentPath = "e:\\" ; String fileName = "news4.txt" ; File file = new File (parentPath, fileName); try { file.createNewFile (); System .out .println ("创建成功~" ); } catch (IOException e) { e.printStackTrace (); } } }
文件相关的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void info () { File file = new File ("e:\\news1.txt" ); System.out.println("文件名字=" + file.getName()); System.out.println("文件绝对路径=" + file.getAbsolutePath()); System.out.println("文件父级目录=" + file.getParent()); System.out.println("文件大小(字节)=" + file.length()); System.out.println("文件是否存在=" + file.exists()); System.out.println("是不是一个文件=" + file.isFile()); System.out.println("是不是一个目录=" + file.isDirectory()); }
目录操作和文件删除 mkdir()创建一级目录,mkdirs()创建多级目录,delete删除空目录或文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class Directory_ { public static void main (String[] args) { } @Test public void m1 () { String filePath = "e:\\news1.txt" ; File file = new File (filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath + "删除成功" ); } else { System.out.println(filePath + "删除失败" ); } } else { System.out.println("该文件不存在..." ); } } @Test public void m2 () { String filePath = "D:\\demo02" ; File file = new File (filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath + "删除成功" ); } else { System.out.println(filePath + "删除失败" ); } } else { System.out.println("该目录不存在..." ); } } @Test public void m3 () { String directoryPath = "D:\\demo\\a\\b\\c" ; File file = new File (directoryPath); if (file.exists()) { System.out.println(directoryPath + "存在.." ); } else { if (file.mkdirs()) { System.out.println(directoryPath + "创建成功.." ); } else { System.out.println(directoryPath + "创建失败..." ); } } } }
IO流原理及流的分类 Java IO流原理
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行
java.io下提供了各种”流“ 类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
输入input:读取外部数据(磁盘,光盘等存储设备的数据到程序中(内存))
输出output:将程序(内存)数据输出到磁盘,光盘等存储到设备中
输入流是读取的,输出流是写入的
流的分类:
按操作数据单位不同分为:字节流(8bit) 二进制文件,字符流(按字符)文本文件,字符对应字节按照编码来查看
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流/包装流
抽象基类
字节流
字符流
输入流
InputStream
Reader
输出流
OutputStream
Writer
java的IO流共涉及40多个类,实际上非常规则,都是从如上四个抽象基类派生的。
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀
.png)
字节输入流常用的类 InputStream常用的子类:
FileInputStream 文件输入流
BufferedInputStream 缓冲字节输入流
ObjectInputStream 对象字节输入流
常用方法
public abstract int read() throws IOException: 一次读取一个字节 返回:下一个数据字节;如果已到达文件末尾,则返回 -1。
public int read(byte[] b) throws IOException:一次读取一个字节数组 (读取实际的字节数) 指定字节数组的长度是:1024或者1024的倍数返回:读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
b - 存储读取数据的缓冲区
public void close() throws IOException:关闭此文件输入流并释放与此流有关的所有系统资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class FileInputStream_ { public static void main (String[] args) { } @Test public void readFile01 () { String filePath = "e:\\hello.txt" ; int readData = 0 ; FileInputStream fileInputStream = null ; try { fileInputStream = new FileInputStream (filePath); while ((readData = fileInputStream.read()) != -1 ) { System.out.print((char )readData); } } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } @Test public void readFile02 () { String filePath = "e:\\hello.txt" ; byte [] buf = new byte [8 ]; int readLen = 0 ; FileInputStream fileInputStream = null ; try { fileInputStream = new FileInputStream (filePath); while ((readLen = fileInputStream.read(buf)) != -1 ) { System.out.print(new String (buf, 0 , readLen)); } } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
FileOutputStream文件输出流 常用方法 public void write(int b) throws IOException: 一次写一个字节 b- 要写入的字节。
public void write(byte[] b) throws IOException: 一次写一个字节数组
public void write(byte[] b, int off,int len) throws IOException: 一次写一部分字节数组
public void close()throws IOException 关闭此文件输出流并释放与此流有关的所有系统资源。此文件输出流不能再用于写入字节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class FileOutputStream01 { public static void main (String[] args) { } @Test public void writeFile () { String filePath = "e:\\a.txt" ; FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream (filePath, true ); String str = "hsp,world!" ; fileOutputStream.write(str.getBytes(), 0 , 3 ); } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
FileReader和FileWriter字符输入输出流 FileReader和FileWriter是字符流,即按照字符来操作io
FileReader相关方法: new FileReader(File/String)
read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
read(char[])批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
new String(char[]) :将char[]转换为String
new String(char[],off,len) :将char[]的指定部分转换成String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @Test public void readFile01 () { String filePath = "e:\\story.txt" ; FileReader fileReader = null ; int data = 0 ; try { fileReader = new FileReader (filePath); while ((data = fileReader.read()) != -1 ) { System.out.print((char ) data); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null ) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void readFile02 () { System.out.println("~~~readFile02 ~~~" ); String filePath = "e:\\story.txt" ; FileReader fileReader = null ; int readLen = 0 ; char [] buf = new char [8 ]; try { fileReader = new FileReader (filePath); while ((readLen = fileReader.read(buf)) != -1 ) { System.out.print(new String (buf, 0 , readLen)); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null ) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } }
FileWriter相关方法: new FileWriter(File/String):覆盖模式,相当于流的指针在首端
new FileWriter(File/String,true) :追加模式,相当于流的指针在尾端
write(int)写入单个字符
write(char[])写入指定数组
write(char[],off,len)写入指定数组的指定部分
write(string)写入整个字符串
write(string,off,len)写入字符串的指定部分
注意:使用后必须要关闭(close)或刷新(flush),否则写入不到指定的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class FileWriter_ { public static void main (String[] args) { String filePath = "e:\\note.txt" ; FileWriter fileWriter = null ; char [] chars = {'a' , 'b' , 'c' }; try { fileWriter = new FileWriter (filePath); fileWriter.write('H' ); fileWriter.write(chars); fileWriter.write("韩顺平教育" .toCharArray(), 0 , 3 ); fileWriter.write("你好北京~" ); fileWriter.write("风雨之后,定见彩虹" ); fileWriter.write("上海天津" , 0 , 2 ); } catch (IOException e) { e.printStackTrace(); } finally { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("程序结束..." ); } }
节点流和处理流 节点流可以从一个特定的数据源读写数据,如FileReader,FileWriter;
处理流(也叫包装流)是连接在已存在的流(节点流和处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader,BufferedWriter.
节点流和处理流表格
分类
字节输入流
字节输出流
字符输入流
字符输出流
抽象基类
InputStream
OutputStream
Reader
Writer
访问文件
FileInputStream
FileOutputStream
FileReader
FileWriter
节点流
访问数组
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
节点流
访问管道
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
节点流
访问字符串
StringReader
StringWriter
节点流
缓冲流
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
处理流
转换流
InputStreamReader
OutputStreamWriter
处理流
对象流
ObjectInputStream
ObjectOutputStream
处理流
抽象基类
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
处理流
打印流
PrintStream
PrintWriter
处理流
退回输入流
PushbackInputStream
PushbackReader
处理流
特殊流
DataInputStream
DataOutputStream
处理流
节点流是底层流/低级流,直接和数据源相接
处理流(包装流)包装节点流,即可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
处理流(也叫包装流)对节点流进行包装,使用修饰器设计模式 ,不会直接与数据源相连
处理流的好处:
性能的提高:主要以增加缓冲的方式来提高输入输出的效率
操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
BufferedReader和BufferedWriter
BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的
关闭时,只需要关闭外层流即可
BufferedReader的演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class BufferedReader_ { public static void main (String[] args) throws Exception { String filePath = "e:\\a.java" ; BufferedReader bufferedReader = new BufferedReader (new FileReader (filePath)); String line; while ((line = bufferedReader.readLine()) != null ) { System.out.println(line); } bufferedReader.close(); } }
BufferedWriter的演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class BufferedWriter_ { public static void main (String[] args) throws IOException { String filePath = "e:\\ok.txt" ; BufferedWriter bufferedWriter = new BufferedWriter (new FileWriter (filePath)); bufferedWriter.write("hello, 韩顺平教育!" ); bufferedWriter.newLine(); bufferedWriter.write("hello2, 韩顺平教育!" ); bufferedWriter.newLine(); bufferedWriter.write("hello3, 韩顺平教育!" ); bufferedWriter.newLine(); bufferedWriter.close(); } }
通过增强流实现文件拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class BufferedCopy_ { public static void main (String[] args) { String srcFilePath = "e:\\a.java" ; String destFilePath = "e:\\a2.java" ; BufferedReader br = null ; BufferedWriter bw = null ; String line; try { br = new BufferedReader (new FileReader (srcFilePath)); bw = new BufferedWriter (new FileWriter (destFilePath)); while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); } System.out.println("拷贝完毕..." ); } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null ) { br.close(); } if (bw != null ) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
这两种都是字节流,在初始化时,会创建一个内部缓冲区数组。
这两种流可以增强二进制文件的读写效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 * 思考:字节流可以操作二进制文件,可以操作文本文件吗?当然可以 */ public class BufferedCopy02 { public static void main (String[] args) { String srcFilePath = "e:\\a.java" ; String destFilePath = "e:\\a3.java" ; BufferedInputStream bis = null ; BufferedOutputStream bos = null ; try { bis = new BufferedInputStream (new FileInputStream (srcFilePath)); bos = new BufferedOutputStream (new FileOutputStream (destFilePath)); byte [] buff = new byte [1024 ]; int readLen = 0 ; while ((readLen = bis.read(buff)) != -1 ) { bos.write(buff, 0 , readLen); } System.out.println("文件拷贝完毕~~~" ); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bis != null ) { bis.close(); } if (bos != null ) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
看一个需求:
将int num = 100 这个int数据保存到文件中,注意不是100数字,而是int 100 并且,能够从文件中直接恢复int 100
将Dog dog = new Dog() 这个对象保存到文件中,并且能够从文件中恢复
上面的需求就是能够将基本数据类型或者对象进行序列化和反序列化操作
序列化和反序列化
序列化就是在保存数据时 ,保存数据的值和数据类型
反序列化就是在恢复数据时 ,恢复数据的值和数据类型
需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable(这是一个标记接口,没有任何方法),Externalizable(该接口有方法需要实现,但是我们一般实现Serializable)
ObjectInputStream和ObjectOutputStream也是包装流(增强流),并且可以传输可序列化的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class ObjectOutStream_ { public static void main (String[] args) throws Exception { String filePath = "e:\\data.dat" ; ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream (filePath)); oos.writeInt(100 ); oos.writeBoolean(true ); oos.writeChar('a' ); oos.writeDouble(9.5 ); oos.writeUTF("韩顺平教育" ); oos.writeObject(new Dog ("旺财" , 10 , "日本" , "白色" )); oos.close(); System.out.println("数据保存完毕(序列化形式)" ); } } class Dog implements Serializable { private String name; private int age; private String nation; private String color; public Dog (String name,int age,String nation,String color) { this .name = name; this .age = age; this .nation = nation; this .color = color; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ObjectInputStream_ { public static void main (String[] args) throws IOException, ClassNotFoundException { String filePath = "e:\\data.dat" ; ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filePath)); System.out.println(ois.readInt()); System.out.println(ois.readBoolean()); System.out.println(ois.readChar()); System.out.println(ois.readDouble()); System.out.println(ois.readUTF()); Object dog = ois.readObject(); System.out.println("运行类型=" + dog.getClass()); System.out.println("dog信息=" + dog); Dog dog2 = (Dog)dog; System.out.println(dog2.getName()); ois.close(); } }
注意:在执行序列化和反序列化中,如果需要序列化的类有所更改,如添加方法或增加字段,需要重新序列化和反序列化一次,否则会报错。
读写顺序要一致
要求实现序列化或反序列化对象,实现Seralizable
序列化的类中建议添加SerialVesionUID,为了提高版本的兼容性,当进行序列化时,如果序列化的类添加字段属性方法,添加序列版本号的类不需要重新序列化一次,会认为是一次版本更新
序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
序列化对象时,要求里面属性的类型也需要实现序列化接口
序列化具备可继承性,也就是说如果某类已经实现了序列化,则他的所有子类也已经默认实现了序列化
标准输入输出流 介绍:
类型
默认设备
System.in 标准输入
InputStream
键盘
System.out 标准输出
PrintStream
显示器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class InputAndOutput { public static void main (String[] args) { System.out.println(System.in.getClass()); System.out.println(System.out.getClass()); System.out.println("hello, 韩顺平教育~" ); Scanner scanner = new Scanner (System.in); System.out.println("输入内容" ); String next = scanner.next(); System.out.println("next=" + next); } }
把一种字节流转为字符流
一个文件乱码问题,引出思考:(transformation)
在默认情况下,读取文件是按照utf-8来读取的
如果文件不是utf-8编码,读取会报错
在转换流可以在读取中将文件按照指定编码来读取文件,使其不会产生乱码的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CodeQuestion { public static void main (String[] args) throws IOException { String filePath = "e:\\a.txt" ; BufferedReader br = new BufferedReader (new FileReader (filePath)); String s = br.readLine(); System.out.println("读取到的内容: " + s); br.close(); } }
inputStreamReader和outputStreamWriter的构造器:
1 2 3 inputStreamReader(InputStream,Charset) //后面可以指定字符编码 outputStreamWriter(OutputStream,Charset)//后面可以指定字符编码
InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class InputStreamReader_ { public static void main (String[] args) throws IOException { String filePath = "e:\\a.txt" ; BufferedReader br = new BufferedReader (new InputStreamReader (new FileInputStream (filePath), "gbk" )); String s = br.readLine(); System.out.println("读取内容=" + s); br.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class OutputStreamWriter_ { public static void main (String[] args) throws IOException { String filePath = "e:\\hsp.txt" ; String charSet = "utf-8" ; OutputStreamWriter osw = new OutputStreamWriter (new FileOutputStream (filePath), charSet); osw.write("hi, 韩顺平教育" ); osw.close(); System.out.println("按照 " + charSet + " 保存文件成功~" ); } }
打印流PrintStream和PrintWriter 打印流只有输出流,没有输入流
printStream的继承图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class PrintStream_ { public static void main (String[] args) throws IOException { PrintStream out = System.out; out.print("john, hello" ); out.write("韩顺平,你好" .getBytes()); out.close(); System.setOut(new PrintStream ("e:\\f1.txt" )); System.out.println("hello, 韩顺平教育~" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class PrintWriter_ { public static void main (String[] args) throws IOException { PrintWriter printWriter = new PrintWriter (new FileWriter ("e:\\f2.txt" )); printWriter.print("hi, 北京你好~~~~" ); printWriter.close(); } }
Properties类 看一个需求
如下配置配置文件:mysql.properties
ip=192.168.0.12
user=root
pwd=1111
请问如何读取ip,user,password的值
使用配置文件可以降低程序的解耦性,将一些程序在启动前的部署配置写到其他文件中。在启动程序自动读取文件,即方便更改而且简单明了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Properties01 { public static void main (String[] args) throws IOException { BufferedReader br = new BufferedReader (new FileReader ("src\\mysql.properties" )); String line = "" ; while ((line = br.readLine()) != null ) { String[] split = line.split("=" ); if ("ip" .equals(split[0 ])) { System.out.println(split[0 ] + "值是: " + split[1 ]); } } br.close(); } }
properties文件:注意不需要再=前后加空格,放置在程序的根目录src下,就可以读取到properties
1 2 3 4 ip=192.168.0.34 user=root pwd=1111
获取value Properties.get(key)**
删除 Properties.remove(key)
修改 Properties.put(key,value)
读取文件 Properties.load(InputStream)
获取属性 Properties.getProperty()
设置属性 Properties.setProperty()
将数据显示到指定设备 Properties.load()
将properties保存到idea中,有中文会存储为unicode Properties.store()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Properties02 { public static void main (String[] args) throws IOException { Properties properties = new Properties (); properties.load(new FileReader ("src\\mysql.properties" )); properties.list(System.out); String user = properties.getProperty("user" ); String pwd = properties.getProperty("pwd" ); System.out.println("用户名=" + user); System.out.println("密码是=" + pwd); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Properties03 { public static void main (String[] args) throws IOException { Properties properties = new Properties (); properties.setProperty("charset" , "utf8" ); properties.setProperty("user" , "汤姆" ); properties.setProperty("pwd" , "888888" ); properties.store(new FileOutputStream ("src\\mysql2.properties" ), null ); System.out.println("保存配置文件成功~" ); } }
网络编程 ip地址 概念:用于唯一标识网络中的每台计算机
查看ip地址:ipconfig
ip地址的表示形式:点分十进制:xx.xx.xx.xx :4个字节(32位)表示
每一个十进制数的范围:0~255
ip地址的组成=网络地址+主机地址,比如:192.168.13.1
IPV6是互联网工程任务组设计的用于替代IPV4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子上编上一个地址,IPV6使用128位表示地址,16个字节,是IPV4的四位
由于IPV4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPV6的使用不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍
ipv4地址分类
类型
范围
形态
A
0.0.0.0 到 127.255.255.255
1 7位网络号 24位主机号
B
128.0.0.0 到 191.255.255.255
10 14位网络号 16位主机号
C
192.0.0.0 到 223.255.255.255
110 21位网络号 8位主机号
D
224.0.0.0 到 239.255.255.255
1110 28位多播组号
E
240.0.0.0 到 247.255.255.255
11110 27位留待后用
特殊的:127.0.0.1表示本机地址
域名 www.baidu.com
好处:为了方便记忆,解决记ip地址的困难
概念:将ip地址映射成域名
端口号 概念:用于标识计算机上某个特定的网络程序
表示形式:以整数形式,范围065535 [2个字节表示端口 02^16-1]
0~1024已经被占用,比如ssh 22 ftp 21 smtp 25 http 80
常见的网络程序端口号:
tomcat:8080
mysql:3306
oracle:1521
sqlserver:1433
网络通信协议 协议tcp/ip TCP/IP(Transmission Control Protocol/Internet Protocol)的简写
中文译名为传输控制协议/因特网互联协议,又叫网络通信协议,这个协议是Internet最基本的协议,Internet国际互联网络的基础,简单的说,就是有网络层的ip协议和传输层的TCP协议组成的
简单来说就是网络世界中程序或服务器互相交流的一种语言(统一的要求或规定,大家都能懂),这就是一种协议。
网络通信协议表格
OSI模型
TCP/IP模型
TCP/IP模型各层对应协议
应用层
应用层
http,ftp,telnet,dns
表示层
应用层
http,ftp,telnet,dns
会话层
应用层
http,ftp,telnet,dns
传输层
传输层
tcp,udp
网络层
网络层
ip,icmp,arp
数据链路层
物理+数据链路层
link
物理层
物理+数据链路层
link
TCP和UDP TCP协议:传输控制协议
使用TCP协议前,须先建立TCP链接,形成传输数据通道
传输前,采用三次握手方式,是可靠的
TCP协议进行通信的两个应用进程:客户端,服务端
在连接中可以进行大数据量的传输
传输完毕需释放已建立的连接,效率低
UDP协议:
将数据,源,目的封装成数据包,不需要建立连接
每个数据报的大小限制在64k内
因无需连接,故是不可靠的
发送数据结束时无需释放资源(因为不是面向连接的),速度快
InetAddresss类
获取本地InetAddress对象getLocalHost
根据指定主机名/域名获取ip地址对象getByName
获取InetAddress对象的主机名getHostName
获取InetAddress对象的地址getHostAddress
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class API_ { public static void main (String[] args) throws UnknownHostException { InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost); InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S" ); System.out.println("host1=" + host1); InetAddress host2 = InetAddress.getByName("www.baidu.com" ); System.out.println("host2=" + host2); String hostAddress = host2.getHostAddress(); System.out.println("host2 对应的ip = " + hostAddress); String hostName = host2.getHostName(); System.out.println("host2对应的主机名/域名=" + hostName); } }
Socket套接字 基于客户端和服务端的网络通信
底层使用的是TCP/IP协议
应用场景距离:客户端发送数据,服务端接收并显示
基于Socket的TCP编程和UDP编程
当我们需要通讯时(读写数据)
1 2 3 4 socket.getOutputStream() socket.getInputStream()
Socket是开发网络应用程序被广泛采用,以至于成为事实上的标准。
通信的两端都要有Socket,是两台机器间通信的关键
网络通信其实就是Socket间的通信
Socket允许程序包网络连接当成一个流,数据在两个Socket之间通过IO传输
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
服务端和客户端通讯过程的简单展示
Server
Client
ServerSocket(int port)
Socket(InetAddress address,int port)
Socket.accept()
Socket.getOutputStream()
Socket.getOutputStream()
Socket.getInputStream()
Socket.getInputStream()
Socket.close()
Socket.close()
完成一个需求:
编写一个服务器端,和一个客户端
服务器端在9999端口监听
客户端连接到服务器端,发送hello,server,然后退出
服务器接收到客户端发送的消息,输出,并退出
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.hspedu.socket;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketTCP01Server { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (9999 ); System.out.println("服务端,在9999端口监听,等待连接.." ); Socket socket = serverSocket.accept(); System.out.println("服务端 socket =" + socket.getClass()); InputStream inputStream = socket.getInputStream(); byte [] buf = new byte [1024 ]; int readLen = 0 ; while ((readLen = inputStream.read(buf)) != -1 ) { System.out.println(new String (buf, 0 , readLen)); } inputStream.close(); socket.close(); serverSocket.close(); } }
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.hspedu.socket;import java.io.IOException;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException;public class SocketTCP01Client { public static void main (String[] args) throws IOException { Socket socket = new Socket (InetAddress.getLocalHost(), 9999 ); System.out.println("客户端 socket返回=" + socket.getClass()); OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello, server" .getBytes()); outputStream.close(); socket.close(); System.out.println("客户端退出....." ); } }
[TCP Socket 编程原理详解 ](https://www.cnblogs.com/FengZeng666/p/15610953.html#:~:text=Socket (套接字) 是网络编程的一种接口,它是一种特殊的 I%2FO。 Socket可以理解为TCP%2FIP网络的API,它定义了许多函数或例程,程序员可以用它们来开发TCP%2FIP网络上的应用程序。 电脑上运行的应用程序通常通过”套接字”向网络发出请求或者应答网络请求。,在 TCP%2FIP 协议中,”IP地址+TCP或UDP端口号”可以唯一标识网络通讯中的一个进程。 可以简单地认为 :”IP地址+端口号”就称为socket。 在TCP协议中,建立连接的两个进程各自有一个socket来标识,这两个 socket组成的socket对就唯一标识一个连接。) 应用案例2
编写一个服务端,和一个客户端
服务端在9999端口监听
客户端连接到服务端,发送“hello,server”,并接收服务器回发的hello,client.再退出
服务端接收到客户端发送的信息,输出,并发送”hello,client”,再退出
注意:在客户端回复服务端发送的消息,因为不确定对方什么时候是将上一次发送或接收的信息处理完成,在回复或再次重发时,需要设置一个结束标记,证明上一条讯息已成功读取或发送完成,才能进行下一次的讯息发送
1 2 socket.shutdownOutput();
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.hspedu.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;@SuppressWarnings({"all"}) public class SocketTCP02Server { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (9999 ); System.out.println("服务端,在9999端口监听,等待连接.." ); Socket socket = serverSocket.accept(); System.out.println("服务端 socket =" + socket.getClass()); InputStream inputStream = socket.getInputStream(); byte [] buf = new byte [1024 ]; int readLen = 0 ; while ((readLen = inputStream.read(buf)) != -1 ) { System.out.println(new String (buf, 0 , readLen)); } OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello, client" .getBytes()); socket.shutdownOutput(); outputStream.close(); inputStream.close(); socket.close(); serverSocket.close(); } }
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.hspedu.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;@SuppressWarnings({"all"}) public class SocketTCP02Client { public static void main (String[] args) throws IOException { Socket socket = new Socket (InetAddress.getLocalHost(), 9999 ); System.out.println("客户端 socket返回=" + socket.getClass()); OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello, server" .getBytes()); socket.shutdownOutput(); InputStream inputStream = socket.getInputStream(); byte [] buf = new byte [1024 ]; int readLen = 0 ; while ((readLen = inputStream.read(buf)) != -1 ) { System.out.println(new String (buf, 0 , readLen)); } inputStream.close(); outputStream.close(); socket.close(); System.out.println("客户端退出....." ); } }
TCP网络编程 应用案例3
编写一个服务端,和一个客户端
服务端在9999端口监听
客户端连接到服务端,发送“hello,server”,并接收服务器回发的“hello,client”,在退出
服务端接收到客户端发送的信息输出,并发送“hello,client”再退出
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.hspedu.socket;import java.io.*;import java.net.ServerSocket;import java.net.Socket;@SuppressWarnings({"all"}) public class SocketTCP03Server { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (9999 ); System.out.println("服务端,在9999端口监听,等待连接.." ); Socket socket = serverSocket.accept(); System.out.println("服务端 socket =" + socket.getClass()); InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader (new InputStreamReader (inputStream)); String s = bufferedReader.readLine(); System.out.println(s); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter (new OutputStreamWriter (outputStream)); bufferedWriter.write("hello client 字符流" ); bufferedWriter.newLine(); bufferedWriter.flush(); bufferedWriter.close(); bufferedReader.close(); socket.close(); serverSocket.close(); } }
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.hspedu.socket;import java.io.*;import java.net.InetAddress;import java.net.Socket;@SuppressWarnings({"all"}) public class SocketTCP03Client { public static void main (String[] args) throws IOException { Socket socket = new Socket (InetAddress.getLocalHost(), 9999 ); System.out.println("客户端 socket返回=" + socket.getClass()); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter (new OutputStreamWriter (outputStream)); bufferedWriter.write("hello, server 字符流" ); bufferedWriter.newLine(); bufferedWriter.flush(); InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader (new InputStreamReader (inputStream)); String s = bufferedReader.readLine(); System.out.println(s); bufferedReader.close(); bufferedWriter.close(); socket.close(); System.out.println("客户端退出....." ); } }
应用案例4
编写一个服务端,和一个客户端
服务端在9999端口监听
客户端连接到服务端,发送一张图片
服务端接收到客户端发送的图片,保存到src下,发送收到图片再退出
客户端接收到服务端发送的收到图片,再退出
该程序要求使用StreamUtils.java
提示:再字符流刷新输入socket中的数据,使用flush(),再字节流中设置结束标记socket.shutdownOutput()即可
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.hspedu.upload;import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class TCPFileUploadServer { public static void main (String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket (8888 ); System.out.println("服务端在8888端口监听...." ); Socket socket = serverSocket.accept(); BufferedInputStream bis = new BufferedInputStream (socket.getInputStream()); byte [] bytes = StreamUtils.streamToByteArray(bis); String destFilePath = "src\\abc.mp4" ; BufferedOutputStream bos = new BufferedOutputStream (new FileOutputStream (destFilePath)); bos.write(bytes); bos.close(); BufferedWriter writer = new BufferedWriter (new OutputStreamWriter (socket.getOutputStream())); writer.write("收到图片" ); writer.flush(); socket.shutdownOutput(); writer.close(); bis.close(); socket.close(); serverSocket.close(); } }
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.hspedu.upload;import java.io.*;import java.net.InetAddress;import java.net.Socket;public class TCPFileUploadClient { public static void main (String[] args) throws Exception { Socket socket = new Socket (InetAddress.getLocalHost(), 8888 ); String filePath = "e:\\abc.mp4" ; BufferedInputStream bis = new BufferedInputStream (new FileInputStream (filePath)); byte [] bytes = StreamUtils.streamToByteArray(bis); BufferedOutputStream bos = new BufferedOutputStream (socket.getOutputStream()); bos.write(bytes); bis.close(); socket.shutdownOutput(); InputStream inputStream = socket.getInputStream(); String s = StreamUtils.streamToString(inputStream); System.out.println(s); inputStream.close(); bos.close(); socket.close(); } }
StreamUtils类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.io.BufferedReader;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;public class StreamUtils { public static byte [] streamToByteArray(InputStream is) throws Exception{ ByteArrayOutputStream bos = new ByteArrayOutputStream (); byte [] b = new byte [1024 ]; int len; while ((len=is.read(b))!=-1 ){ bos.write(b, 0 , len); } byte [] array = bos.toByteArray(); bos.close(); return array; } public static String streamToString (InputStream is) throws Exception{ BufferedReader reader = new BufferedReader (new InputStreamReader (is)); StringBuilder builder= new StringBuilder (); String line; while ((line=reader.readLine())!=null ){ builder.append(line+"\r\n" ); } return builder.toString(); } }
netstat指令
netstat -an 可以查看当前主机的网络情况,包括端口监听 情况和网络连接 情况
netstat -an | more 可以分页显示
要求再dos控制台 下执行
Listening表示某个端口在监听
如果有一个外部程序连接到该端口,就会显示一条连接信息
ctrl+c退出指令
想要查看是哪个程序在占用某个端口,需要使用管理员命令提示符,输入 netstat -anb
TCP网络通讯的秘密 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP来分配的
1 2 3 TCP 192.168.12.1:8888 192.168.12.1:60285 ESTABLISHED //这个60285就是TCP/IP分配给客户端的端口 TCP 102.168.12.1:60285 192.168.12.1:8888 ESTABLISHED //由于是一台主机,所以两个端口都可见
UDP网络编程
类DatagramSocket和DatagramPacket [数据包,数据报] 实现了基于UDP协议网络程序
UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不确定什么时候可以抵达
DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接
发送接收的流程图
UDP说明:
接收数据和发送数据是通过DatagramSocket对象来完成的
将数据封装到DatagramPacket对象装包,发送数据
当接收到DatagramPacket对象,需要进行拆包,取出数据
DatagramSocket可以指定在哪个端口接收数据
发送基本流程
核心的两个类/对象 DatagramSocket和DatagramPacket
建立发送端,接收端(没有服务端和客户端概念)
发送数据前,建立数据包/报 DatagramPacket对象
调用DatagramSocket的发送,接收方法
关闭DatagramSocket
1 2 3 4 5 6 7 8 9 datagramPacket的构造器 DatagramPacket(byte[],int,int)//字节数组,开始长度,结束长度 DatagramPacket(byte[],int) DatagramPacket(byte[],int,int,InetAddress,int) //字节数组,开始长度,结束长度,网络地址,端口 DatagramPacket(byte[],int,int,SocketAddress) DatagramPacket(byte[],int,InetAddress,int) DatagramPacket(byte[],int,SocketAddress)
应用案例
编写一个接收端A(Receiver),和一个发送端(Sender)
接收端A在9999端口等待接收数据(receive)
发送端A向接收端B发送数据“hello,明天吃火锅”
接收端B接收到发送端A发送的数据,回复“好的,明天见”,再退出
发送端接收回复的数据,就退出
接收端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.hspedu.udp;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;public class UDPReceiverA { public static void main (String[] args) throws IOException { DatagramSocket socket = new DatagramSocket (9999 ); byte [] buf = new byte [1024 ]; DatagramPacket packet = new DatagramPacket (buf, buf.length); System.out.println("接收端A 等待接收数据.." ); socket.receive(packet); int length = packet.getLength(); byte [] data = packet.getData(); String s = new String (data, 0 , length); System.out.println(s); data = "好的, 明天见" .getBytes(); packet = new DatagramPacket (data, data.length, InetAddress.getByName("192.168.12.1" ), 9998 ); socket.send(packet); socket.close(); System.out.println("A端退出..." ); } }
发送端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.hspedu.udp;import java.io.IOException;import java.net.*;@SuppressWarnings({"all"}) public class UDPSenderB { public static void main (String[] args) throws IOException { DatagramSocket socket = new DatagramSocket (9998 ); byte [] data = "hello 明天吃火锅~" .getBytes(); DatagramPacket packet = new DatagramPacket (data, data.length, InetAddress.getByName("192.168.12.1" ), 9999 ); socket.send(packet); byte [] buf = new byte [1024 ]; packet = new DatagramPacket (buf, buf.length); socket.receive(packet); int length = packet.getLength(); data = packet.getData(); String s = new String (data, 0 , length); System.out.println(s); socket.close(); System.out.println("B端退出" ); } }
反射 反射机制 一个需求引出反射
根据配置文件re.properties指定信息,创建对象并调用方法
classfullpath = com.hspedu.Cat
method=hi
创建Cat对象并调用hi方法?
这样的需求再学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则 ),指的是在不修改源码的情况下,来扩容扩展功能
re.properties
1 2 3 classfullpath =com.hspedu.Cat method =hi
Cat类
1 2 3 4 5 6 7 8 9 10 11 12 public class Cat { private String name = "招财猫" ; public int age = 10 ; public Cat () {} public Cat (String name) { this .name = name; } public void hi () { Syste.out.println("hi," + name); } }
主类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) throws throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Cat cat = new Cat (); cat.hi(); Properties properties = new Properties (); properties.load(new FileInputStream ("src\\re.properties" )); String classFullPath = properties.get("classfullpath" ).toString(); String method = properties.get("method" ).toString(); System.out.println("classfullPath=" + classFullPath); System.out.println("method=" + method); new classFullPath () Class aclass = Class.forName(classFullPath); Object o = aclass.newInstance(); System.out.println(o.getClass()); Method method = aclass.getMethod(method); System.out.println("=========================" ); method.invoke(o); }
反射机制解释
反射机制允许程序在执行十七借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和底层框架都会用到。
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整构造信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
java反射机制原理的图
java反射机制可以完成
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时得到任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理
反射的主要类 java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method :代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法,Constructor表示构造器对象
这些类在java.lang.reflect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.hspedu.reflection;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Properties;public class Reflection01 { public static void main (String[] args) throws Exception { Properties properties = new Properties (); properties.load(new FileInputStream ("src\\re.properties" )); String classfullpath = properties.get("classfullpath" ).toString(); String methodName = properties.get("method" ).toString(); Class cls = Class.forName(classfullpath); Object o = cls.newInstance(); System.out.println("o的运行类型=" + o.getClass()); Method method1 = cls.getMethod(methodName); System.out.println("=============================" ); method1.invoke(o); Field nameField = cls.getField("age" ); System.out.println(nameField.get(o)); Constructor constructor = cls.getConstructor(); System.out.println(constructor); Constructor constructor2 = cls.getConstructor(String.class); System.out.println(constructor2); } }
反射优点和缺点 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
缺点:使用反射基本是解释执行,对执行速度有影响
缺点可以用反射调用优化-关闭访问检查来弥补
Method和Field,Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问开关检查的开关
参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.hspedu.reflection;import com.hspedu.Cat;import java.io.FileInputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Reflection02 { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { m1(); m2(); m3(); } public static void m1 () { Cat cat = new Cat (); long start = System.currentTimeMillis(); for (int i = 0 ; i < 90 ; i++) { cat.hi(); } long end = System.currentTimeMillis(); System.out.println("m1() 耗时=" + (end - start)); } public static void m2 () throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat" ); Object o = cls.newInstance(); Method hi = cls.getMethod("hi" ); long start = System.currentTimeMillis(); for (int i = 0 ; i < 900000000 ; i++) { hi.invoke(o); } long end = System.currentTimeMillis(); System.out.println("m2() 耗时=" + (end - start)); } public static void m3 () throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat" ); Object o = cls.newInstance(); Method hi = cls.getMethod("hi" ); hi.setAccessible(true ); long start = System.currentTimeMillis(); for (int i = 0 ; i < 900000000 ; i++) { hi.invoke(o); } long end = System.currentTimeMillis(); System.out.println("m3() 耗时=" + (end - start)); } }
Class类
Class类也是类,因此也继承Object类
Class类对象不是new出来的,而是系统创建的
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整的得到一个类的完整结构,通过一系列API
Class类对象是存放在堆中的
对class类和方法的演示类图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.hspedu.reflection.class_;import com.hspedu.Cat;import java.util.ArrayList;public class Class01 { public static void main (String[] args) throws ClassNotFoundException { Class cls1 = Class.forName("com.hspedu.Cat" ); Class cls2 = Class.forName("com.hspedu.Cat" ); System.out.println(cls1.hashCode()); System.out.println(cls2.hashCode()); Class cls3 = Class.forName("com.hspedu.Dog" ); System.out.println(cls3.hashCode()); } }
Car类
1 2 3 4 5 6 public class Car { private String brand = "宝马" ; public int piece = 500000 ; public String color = "白色" ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.hspedu.reflection.class_;import com.hspedu.Car;import java.lang.reflect.Field;public class Class02 { public static void main (String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { String classAllPath = "com.hspedu.Car" ; Class<?> cls = Class.forName(classAllPath); System.out.println(cls); System.out.println(cls.getClass()); System.out.println(cls.getPackage().getName()); System.out.println(cls.getName()); Car car = (Car) cls.newInstance(); System.out.println(car); Field brand = cls.getField("brand" ); System.out.println(brand.get(car)); brand.set(car, "奔驰" ); System.out.println(brand.get(car)); System.out.println("=======所有的字段属性====" ); Field[] fields = cls.getFields(); for (Field f : fields) { System.out.println(f.getName()); } } }
获取Class对象的方式 前提:已知一个类的全类名,且在该类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例Class aclass = Class.forName(“java.lang.Cat”);
应用场景 :多用于配置文件,读取类全路径,加载类
前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高实例:Class aclass = Cat.class;
应用场景 :多用于参数传递,比如通过反射到对应的构造器对象
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:Class aclass = 对象.getClass();
应用场景 :通过创建好的对象,获取Class对象
其他方式
ClassLoader cl = 对象.getClass().getClassLoader();
Class aclass = cl.loadClass(“类的全类名”);
演示获取类的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.hspedu.reflection.class_;import com.hspedu.Car;public class GetClass_ { public static void main (String[] args) throws ClassNotFoundException { String classAllPath = "com.hspedu.Car" ; Class<?> cls1 = Class.forName(classAllPath); System.out.println(cls1); Class cls2 = Car.class; System.out.println(cls2); Car car = new Car (); Class cls3 = car.getClass(); System.out.println(cls3); ClassLoader classLoader = car.getClass().getClassLoader(); Class cls4 = classLoader.loadClass(classAllPath); System.out.println(cls4); System.out.println(cls1.hashCode()); System.out.println(cls2.hashCode()); System.out.println(cls3.hashCode()); System.out.println(cls4.hashCode()); Class<Integer> integerClass = int .class; Class<Character> characterClass = char .class; Class<Boolean> booleanClass = boolean .class; System.out.println(integerClass); Class<Integer> type1 = Integer.TYPE; Class<Character> type2 = Character.TYPE; System.out.println(type1); System.out.println(integerClass.hashCode()); System.out.println(type1.hashCode()); } }
哪些类型有Class对象
外部类,成员内部类,静态内部类,局部内部类,匿名内部类
interface:接口
数组
enum:枚举
annotation:注解
基本数据类型
void
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.hspedu.reflection.class_;import java.io.Serializable;public class AllTypeClass { public static void main (String[] args) { Class<String> cls1 = String.class; Class<Serializable> cls2 = Serializable.class; Class<Integer[]> cls3 = Integer[].class; Class<float [][]> cls4 = float [][].class; Class<Deprecated> cls5 = Deprecated.class; Class<Thread.State> cls6 = Thread.State.class; Class<Long> cls7 = long .class; Class<Void> cls8 = void .class; Class<Class> cls9 = Class.class; System.out.println(cls1); System.out.println(cls2); System.out.println(cls3); System.out.println(cls4); System.out.println(cls5); System.out.println(cls6); System.out.println(cls7); System.out.println(cls8); System.out.println(cls9); } }
类加载机制 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
动态加载:运行该时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
类加载时机
当创建对象时new
当子类被加载时
调用类中的静态成员时
通过反射Class.forName(“com.test.Cat”);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.*;import java.lang.reflect.*;public class ClasLoad_ { public static void main (String[] args) throws Exception { Scanner sc = new Scanner (System.in); System.out.println("请输入key:" ); String key = sc.next(); switch (key){ case "1" : Dog dog = new Dog (); dog.cry(); break ; case "2" : Class aclass = Class.forName("Person" ); Object 0 = aclass.newInstance(); Method m = aclsss.getMethod("hi" ); m.invoke(o); System.out.println("ok" ); break ; default : System.out.println("do nothing..." ); } } } class Dog { public void cry () { System.out.println("小狗汪汪叫" ); } }
加载阶段
JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
连接阶段 -验证
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
包括:文件格式验证(是否以魔数开头:oxcafebabe),元数据验证,字节码验证和符号引用验证
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证,缩短虚拟机类加载的时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.hspedu.reflection.classload_;public class ClassLoad02 { public static void main (String[] args) { } } class A { public int n1 = 10 ; public static int n2 = 20 ; public static final int n3 = 30 ; }
连接阶段 -解析
虚拟机将常量池内的符号引用替换为直接引用的过程。
JVM 链接阶段之 Resolution——解析
Initialization 初始化
到初始化阶段,才真正开始执行类中定义的java程序代码,此阶段是执行()方法的过程
()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有的静态变量的赋值动作和静态代码块中的语句,并进行合并。
虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的() 方法,其他线程都需要阻塞等待,直到活动线程执行() 方法完毕
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.hspedu.reflection.classload_;public class ClassLoad03 { public static void main (String[] args) throws ClassNotFoundException { B b = new B (); } } class B { static { System.out.println("B 静态代码块被执行" ); num = 300 ; } static int num = 100 ; public B () { System.out.println("B() 构造器被执行" ); } }
获取类结构信息
getName()获取全类名
getSimpleName()获取简单类名
getFields()获取所有public修饰的属性,包含本类以及父类的
getDeclaredFields()获取本类中所有属性
getMethods()获取所有public修饰的方法,包含本类以及父类的
getDeclaredMethods()获取本类中所有方法
getConstructors()获取所有public修饰的构造器,包含本类的
getDeclaredConstructors()获取本类中所有的构造器
getpackage()以Package形式返回包信息
getSuperClass以Class形式返回父类信息
getInterfaces()以Class形式返回接口信息
getAnnotations()以Annotation[]形式返回注解信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 package com.hspedu.reflection;import org.junit.jupiter.api.Test;import java.lang.annotation.Annotation;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectionUtils { public static void main (String[] args) { } @Test public void api_02 () throws ClassNotFoundException, NoSuchMethodException { Class<?> personCls = Class.forName("com.hspedu.reflection.Person" ); Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本类中所有属性=" + declaredField.getName() + " 该属性的修饰符值=" + declaredField.getModifiers() + " 该属性的类型=" + declaredField.getType()); } Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本类中所有方法=" + declaredMethod.getName() + " 该方法的访问修饰符值=" + declaredMethod.getModifiers() + " 该方法返回类型" + declaredMethod.getReturnType()); Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("该方法的形参类型=" + parameterType); } } Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("====================" ); System.out.println("本类中所有构造器=" + declaredConstructor.getName()); Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("该构造器的形参类型=" + parameterType); } } } @Test public void api_01 () throws ClassNotFoundException, NoSuchMethodException { Class<?> personCls = Class.forName("com.hspedu.reflection.Person" ); System.out.println(personCls.getName()); System.out.println(personCls.getSimpleName()); Field[] fields = personCls.getFields(); for (Field field : fields) { System.out.println("本类以及父类的属性=" + field.getName()); } Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本类中所有属性=" + declaredField.getName()); } Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println("本类以及父类的方法=" + method.getName()); } Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本类中所有方法=" + declaredMethod.getName()); } Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("本类的构造器=" + constructor.getName()); } Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("本类中所有构造器=" + declaredConstructor.getName()); } System.out.println(personCls.getPackage()); Class<?> superclass = personCls.getSuperclass(); System.out.println("父类的class对象=" + superclass); Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println("接口信息=" + anInterface); } Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("注解信息=" + annotation); } } } class A { public String hobby; public void hi () { } public A () { } public A (String name) { } } interface IA {} interface IB {} @Deprecated class Person extends A implements IA , IB { public String name; protected static int age; String job; private double sal; public Person () { } public Person (String name) { } private Person (String name, int age) { } public void m1 (String name, int age, double sal) { } protected String m2 () { return null ; } void m3 () { } private void m4 () { } }
通过反射获取对象 方式一:调用类中的public修饰的无参构造器
方式二:调用类中的指定构造器
Class相关的方法:
newInstance:调用类中的午餐构造器,获取对应类的都西昂
getConstructor(Class…clazz)根据参数列表,获取对应的public构造器对象
getDecalaredConstructor(Class…clazz)根据参数列表,获取对应的所有构造器对象
Constructor类相关方法
setAccessible:爆破
newInstance(Object..obj)调用构造器
演示反射获取私有构造器并赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.hspedu.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class ReflecCreateInstance { public static void main (String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<?> userClass = Class.forName("com.hspedu.reflection.User" ); Object o = userClass.newInstance(); System.out.println(o); Constructor<?> constructor = userClass.getConstructor(String.class); Object hsp = constructor.newInstance("hsp" ); System.out.println("hsp=" + hsp); Constructor<?> constructor1 = userClass.getDeclaredConstructor(int .class, String.class); constructor1.setAccessible(true ); Object user2 = constructor1.newInstance(100 , "张三丰" ); System.out.println("user2=" + user2); } } class User { private int age = 10 ; private String name = "韩顺平教育" ; public User () { } public User (String name) { this .name = name; } private User (int age, String name) { this .age = age; this .name = name; } public String toString () { return "User [age=" + age + ", name=" + name + "]" ; } }
演示反射获取私有属性并赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.hspedu.reflection;import java.lang.reflect.Field;public class ReflecAccessProperty { public static void main (String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { Class<?> stuClass = Class.forName("com.hspedu.reflection.Student" ); Object o = stuClass.newInstance(); System.out.println(o.getClass()); Field age = stuClass.getField("age" ); age.set(o, 88 ); System.out.println(o); System.out.println(age.get(o)); Field name = stuClass.getDeclaredField("name" ); name.setAccessible(true ); name.set(null , "老韩~" ); System.out.println(o); System.out.println(name.get(o)); System.out.println(name.get(null )); } } class Student { public int age; private static String name; public Student () { } public String toString () { return "Student [age=" + age + ", name=" + name + "]" ; } }
通过反射获取私有方法并使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package com.hspedu.reflection;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class ReflecAccessMethod { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss" ); Object o = bossCls.newInstance(); Method hi = bossCls.getDeclaredMethod("hi" , String.class); hi.invoke(o, "韩顺平教育~" ); Method say = bossCls.getDeclaredMethod("say" , int .class, String.class, char .class); say.setAccessible(true ); System.out.println(say.invoke(o, 100 , "张三" , '男' )); System.out.println(say.invoke(null , 200 , "李四" , '女' )); Object reVal = say.invoke(null , 300 , "王五" , '男' ); System.out.println("reVal 的运行类型=" + reVal.getClass()); Method m1 = bossCls.getDeclaredMethod("m1" ); Object reVal2 = m1.invoke(o); System.out.println("reVal2的运行类型=" + reVal2.getClass()); } } class Monster {}class Boss { public int age; private static String name; public Boss () { } public Monster m1 () { return new Monster (); } private static String say (int n, String s, char c) { return n + " " + s + " " + c; } public void hi (String s) { System.out.println("hi " + s); } }