多线程

正常来说代码执行顺序从上到下

1,通过断点调试运行
右键添加Toggle Breakpoint,需要再Debug模式运行(点击小虫子或者右键Debug As)
Resume(F8)继续运行直到遇到其他断点
Step Into(F5)会进入方法内部
Step over(F5)

2,进程和线程
一个程序就是一个进程

3,我们的计算机是支持多进程的

4,同理多线程有什么作用
以游戏为例子(需要一个线程来控制主角移动,一个线程控制敌人的AI攻击行为)
当我们需要多个任务同时运行的时候,就可以使用多线程

5,主线程
我们以前平时写的java程序都是单线程的,都是从main方法执行的,main方法处于一个
默认的线程中,这个线程称为主线程。主线程是系统默认系统创建出来的。(JVM创建启动)

6,创建线程(方法一)不常用
a)创建MyThread类,继承Thread
b)重写run方法

  1. public class MyThread extends Thread {
  2. public MyThread() {}
  3. public MyThread(String name) {
  4. super(name);
  5. }
  6. @Override
  7. public void run() {
  8. for(int i=0;i<100;i++) {
  9. if(interrupted()) {//当前线程被设置为中断状态
  10. //做一些事情
  11. System.out.println("释放资源");
  12. break;
  13. }
  14. System.out.println(getName()+":" +i);
  15. }
  16. }
  17. }
  1. c)创建对象,并调用start方法运行<br /> (如果直接调用run方法,相当于调用普通方法,并不会启动线程去调用run)<br /> <br />其他<br /> a)获取线程名字 thread.getName(),设置名字 thread.setName("线程1")或者构造Thread对象时直接设置<br /> b)如何获取主线程(main所在线程) Thread mainThread = Thread.currentThread()<br /> <br />**线程调度规则**<br />(线程调度会整体上是遵守下面的规则,但是从单个上来看是随机的)
  • 分时调度(平均分配)
  • 抢占式调度(按照优先级)(Java使用的调度规则)

    优先级高的,有更高的几率被CPU所执行
    c)设置优先级 setPriority 得到优先级 getPriority
    d)线程休眠,让当前线程休眠 Thread.sleep();
    e)join()线程加入 把某个线程加入(合并)到当前线程中,
    f)设置守护线程 setDaemon(类似C#的前台线程后台线程)如果程序中只剩下守护线程在运行,那么程序会停止
    g)线程中断 强制性杀死
    stop()被弃用 直接强制杀死 被动
    推荐 interrupt()
    thread.interrupt()设置线程中断状态,并不是真正中断了。需要主动去判断状态interrupted(),在线程类中根据状态做一些事情
    让线程自己抛出异常,让线程自己可以处理被终止的时候做一些事情
    ```java package com.sikiedu.chapter1;

public class Demo01_CreateThread { public static void main(String[] args) { System.out.println(Thread.NORM_PRIORITY);//默认优先级5 System.out.println(Thread.MIN_PRIORITY);//最小优先级1 System.out.println(Thread.MAX_PRIORITY);//最大优先级10

  1. Thread mainThread = Thread.currentThread();
  2. mainThread.setPriority(10);
  3. MyThread thread = new MyThread();
  4. System.out.println(mainThread.getPriority());
  5. System.out.println(thread.getPriority());

// thread.run();//error 相当于普通方法调用 thread.start();//启动线程

  1. int j = 0;
  2. for(int i=0;i<10000000;i++) {
  3. j+=i;
  4. }
  5. System.out.println("MainThread"+System.currentTimeMillis());
  6. }

}

  1. ```java
  2. package com.sikiedu.chapter1;
  3. public class Demo02_Thread {
  4. public static void main(String[] args) throws Exception {
  5. // try {
  6. // Thread.sleep(4000);
  7. // } catch (InterruptedException e) {
  8. // // TODO Auto-generated catch block
  9. // e.printStackTrace();
  10. // }
  11. MyThread t1 = new MyThread("线程1");
  12. // System.out.println("ThreadT1Name:"+t1.getName());
  13. t1.start();
  14. Thread.sleep(1);
  15. // t1.stop();
  16. t1.interrupt();//
  17. // t1.join();//当t1加入到当前线程中,就是加入到主线程中,就是放到主线程中去继续执行
  18. //t1.start();//不可以重复调用start
  19. MyThread t2 = new MyThread("线程2");
  20. // System.out.println("ThreadT2Name:"+t2.getName());
  21. // t2.setDaemon(true);//需要再启动线程前设置守护线程
  22. t2.start();
  23. System.out.println("Main end");
  24. }
  25. }

7,线程生命周期:https://blog.csdn.net/lonelyroamer/article/details/7949969

8,创建线程(方法二)常用
a)实现Runnable接口
b)实现run方法
c)创建当前MyRunnable类的对象和Thread的对象,并使用thread对象启动线程

  1. package com.sikiedu.chapter1;
  2. public class MyRunnable implements Runnable {
  3. private String data = "";
  4. @Override
  5. public void run() {
  6. for(int i=0;i<100;i++) {
  7. Thread t = Thread.currentThread();
  8. System.out.println(t.getName()+":"+i);
  9. }
  10. }
  11. }
  1. package com.sikiedu.chapter1;
  2. // extends Thread
  3. // implements Runnable
  4. public class Demo03_CreateThreadMethod2 {
  5. public static void main(String[] args) {
  6. MyRunnable t = new MyRunnable();
  7. Thread t1 = new Thread(t,"线程1");
  8. t1.start();
  9. Thread t2 = new Thread(t,"线程2");
  10. t2.start();
  11. }
  12. }
  1. 其他<br /> a)获取当前线程的名字<br /> Thread.currentThread().getName()<br /> 设置名字通过Thread对象<br /> b)构造方法<br /> Thread t = new Thread(Runnable target);<br /> Thread t = new Thread(Runnable target,String name);

9,方式二的好处
可以避免单继承带来的局限性(实现了接口后,可以继承一个别的类)
可以很好的处理两个线程共享一个资源的情况(数据共享)。

10,创建线程(方法三)
依据方法一和二,使用匿名内部类
a)new Runnable(){}
b)new Thread(){}

  1. package com.sikiedu.chapter1;
  2. public class Demo04_CreateThread {
  3. public static void main(String[] args) {
  4. // // 匿名内部类
  5. // Runnable r = new Runnable() {
  6. //
  7. // @Override
  8. // public void run() {
  9. // for(int i = 0;i<100;i++) {
  10. // System.out.println(Thread.currentThread().getName()+":"+i);
  11. // }
  12. // }
  13. //
  14. // };
  15. //
  16. //// Thread t = new Thread(r,"匿名内部类线程");
  17. //// t.start();
  18. // new Thread(r,"匿名内部类线程").start();
  19. new Thread() {
  20. @Override
  21. public void run() {
  22. for(int i = 0;i<100;i++) {
  23. System.out.println(Thread.currentThread().getName()+":"+i);
  24. }
  25. }
  26. }.start();
  27. }
  28. }

11,我们用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。
方法一:

  1. package com.sikiedu.chapter1;
  2. public class TicketThread extends Thread{
  3. private static int count = 100;//所有线程应该一共有100张票,而不是每个售票点对象都有100张票,所以票数应设置成static
  4. public TicketThread(String name) {
  5. super(name);
  6. }
  7. @Override
  8. public void run() {
  9. while(true) {
  10. if(count>0) {
  11. System.out.println(getName()+ "卖出第"+count+"张票");
  12. count--;
  13. }else {
  14. break;
  15. }
  16. }
  17. }
  18. }
  1. TicketThread t1 = new TicketThread("售票点1");
  2. TicketThread t2 = new TicketThread("售票点2");
  3. TicketThread t3 = new TicketThread("售票点3");
  4. TicketThread t4 = new TicketThread("售票点4");
  5. t1.start();
  6. t2.start();
  7. t3.start();
  8. t4.start();

问题:count即使设置成static后依然会出现重复卖同一张票的情况。
方法二:

  1. package com.sikiedu.chapter1;
  2. public class TicketRunnable implements Runnable {
  3. private int count= 100;
  4. @Override
  5. public void run() {
  6. while(count>0) {
  7. if(count>0) {//线程1 线程2
  8. System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100
  9. count--;
  10. }else {
  11. break;
  12. }
  13. }
  14. }
  15. }
  1. TicketRunnable t = new TicketRunnable();
  2. Thread t1 = new Thread(t,"售票点1");
  3. Thread t2 = new Thread(t,"售票点2");
  4. Thread t3 = new Thread(t,"售票点3");
  5. Thread t4 = new Thread(t,"售票点4");
  6. t1.start();
  7. t2.start();
  8. t3.start();
  9. t4.start();

问题:count即使设置成static后依然会出现重复卖同一张票的情况。

12,线程安全问题
售票问题原因:多个线程同时要修改一个变量的时候,引起冲突
线程安全问题解决:
synchronized(对象){//锁住某个对象,如果这个对象已经被锁定,那么等待。

}

  1. package com.sikiedu.chapter1;
  2. public class TicketThread extends Thread{
  3. private static int count = 100;//所有线程应该一共有100张票,而不是每个售票点对象都有100张票,所以票数应设置成static
  4. private static Object lock = new Object();
  5. public TicketThread(String name) {
  6. super(name);
  7. }
  8. @Override
  9. public void run() {
  10. while(true) {
  11. synchronized (lock) {//这里lock不可以换成this,因为每个this各代表一个对象
  12. if(count>0) {
  13. System.out.println(getName()+ "卖出第"+count+"张票");
  14. count--;
  15. }else {
  16. break;
  17. }
  18. }
  19. }
  20. }
  21. }
  1. package com.sikiedu.chapter1;
  2. public class TicketRunnable implements Runnable {
  3. private int count= 100;
  4. private Object lock = new Object();
  5. @Override
  6. public void run() {
  7. while(true) {
  8. //这里lock可以换成this
  9. synchronized (lock) {//第一个线程来的 时候会锁上,并拿走钥匙,第二个线程来的 时候,发现被锁上了,等待
  10. if(count>0) {//线程1 线程2
  11. System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100
  12. count--;
  13. }else {
  14. break;
  15. }
  16. }//执行完代码,归还钥匙
  17. try {
  18. Thread.sleep(100);
  19. } catch (InterruptedException e) {
  20. // TODO Auto-generated catch block
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. }

a)出现线程安全问题的地方,要锁同一个对象(可以是当前对象this,也可以单独创建一个Object对象lock,只限方法二)

b)锁住某个对象,如果这个对象已经被锁定,那么停止当前线程的执行,一直等待,一直等到对象被解锁。
(保证同一个时间,只有一个线程在使用这个对象,)

c)创建同步方法
去掉synchronized (this),把方法声明为synchronized
同步方法锁的是哪个对象呢?锁定的是当前对象this

  1. package com.sikiedu.chapter1;
  2. public class TicketRunnable implements Runnable {
  3. private int count= 100;
  4. private Object lock = new Object();
  5. @Override
  6. public void run() {
  7. while(count>0) {
  8. sellTicket();
  9. try {
  10. Thread.sleep(100);
  11. } catch (InterruptedException e) {
  12. // TODO Auto-generated catch block
  13. e.printStackTrace();
  14. }
  15. }
  16. }
  17. public synchronized void sellTicket() {
  18. // synchronized (this) {//第一个线程来的 时候会锁上,并拿走钥匙,第二个线程来的 时候,发现被锁上了,等待
  19. if(count>0) {//线程1 线程2
  20. System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100
  21. count--;
  22. }
  23. // }//执行完代码,归还钥匙
  24. }
  25. }

13,线程安全的类
安全: StringBuffer(查看源码会发现方法声明为synchronized。所以线程安全,不过效率略低。其他类同理) Vector
不安全:StringBuilder ArrayList

14,同步锁的第二种使用方式
a)创建锁对象 ReentrantLock lock = new ReentrantLock();
b)加锁和解锁
使用try finally
lock.lock();加锁
lock.unlock();解锁

  1. package com.sikiedu.chapter1;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class TicketRunnable implements Runnable {
  4. private int count= 100;
  5. private ReentrantLock lock = new ReentrantLock();
  6. @Override
  7. public void run() {
  8. while(count>0) {
  9. lock.lock();//加锁
  10. try {
  11. if(count>0) {//线程1 线程2
  12. System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100
  13. count--;
  14. }
  15. } finally {
  16. //把unlock()解锁放在finally里,保证即使需要加锁的代码中出现错误也能执行到unlock()
  17. lock.unlock();
  18. }
  19. try {
  20. Thread.sleep(100);
  21. } catch (InterruptedException e) {
  22. // TODO Auto-generated catch block
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

15,线程安全问题的第二个例子
买票问题升级(两种卖票方式,一种通过电影院窗口,一种通过手机App)
问:之前是在一个Runnable对象中进行线程安全的处理。现在是两个Runnable对象,那该怎么保证是同一个锁呢?
答:定义一个成员变量,通过构造方法把锁对象传入即可

  1. package com.sikiedu.chapter1;
  2. public class TicketMng {
  3. public static int count = 100;
  4. }
  1. package com.sikiedu.chapter1;
  2. public class WindowThread implements Runnable {
  3. public WindowThread(Object lock) {
  4. this.lock=lock;
  5. }
  6. private Object lock;
  7. @Override
  8. public void run() {
  9. while( TicketMng.count>0 ) {
  10. synchronized (lock) {
  11. if(TicketMng.count>0) {
  12. System.out.println(Thread.currentThread().getName()+"售出第"+TicketMng.count+"张票");
  13. TicketMng.count--;
  14. }
  15. }
  16. try {
  17. Thread.sleep(2);
  18. } catch (InterruptedException e) {
  19. // TODO Auto-generated catch block
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. }
  1. package com.sikiedu.chapter1;
  2. public class AppThread implements Runnable {
  3. public AppThread(Object lock) {
  4. this.lock=lock;
  5. }
  6. private Object lock;
  7. @Override
  8. public void run() {
  9. while( TicketMng.count>0 ) {
  10. synchronized (lock) {
  11. if(TicketMng.count>0) {
  12. System.out.println(Thread.currentThread().getName()+"售出第"+TicketMng.count+"张票");
  13. TicketMng.count--;
  14. }
  15. }
  16. try {
  17. Thread.sleep(1);
  18. } catch (InterruptedException e) {
  19. // TODO Auto-generated catch block
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. }
  1. package com.sikiedu.chapter1;
  2. public class Demo06_Practice {
  3. public static void main(String[] args) {
  4. Object lock = new Object();
  5. WindowThread windowThread = new WindowThread(lock);
  6. AppThread appThread = new AppThread(lock);
  7. new Thread(windowThread,"窗口").start();
  8. new Thread(appThread,"手机App").start();
  9. }
  10. }

16,死锁问题
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

  1. package com.sikiedu.chapter1;
  2. public class Demo07_DeadLock {
  3. public static Object lock1 = new Object();
  4. public static Object lock2 = new Object();
  5. public static void main(String[] args) {
  6. new Thread(new Thread1()).start();
  7. new Thread(new Thread2()).start();
  8. }
  9. }
  10. class Thread1 implements Runnable{
  11. @Override
  12. public void run() {
  13. synchronized (Demo07_DeadLock.lock1) {
  14. System.out.println("取得第一把锁之后要做的事情");
  15. try {
  16. Thread.sleep(100);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. synchronized (Demo07_DeadLock.lock2) {
  22. System.out.println("Thread1同时取得两把锁之后要做的事情");
  23. }
  24. }
  25. }
  26. }
  27. class Thread2 implements Runnable{
  28. @Override
  29. public void run() {
  30. synchronized (Demo07_DeadLock.lock2) {
  31. System.out.println("取得第二把锁之后要做的事情");
  32. try {
  33. Thread.sleep(100);
  34. } catch (InterruptedException e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37. }
  38. synchronized (Demo07_DeadLock.lock1) {
  39. System.out.println("Thread2同时取得两把锁之后要做的事情");
  40. }
  41. }
  42. }
  43. }

解决方法:锁的顺序设置相同

  1. package com.sikiedu.chapter1;
  2. public class Demo07_DeadLock {
  3. public static Object lock1 = new Object();
  4. public static Object lock2 = new Object();
  5. public static void main(String[] args) {
  6. new Thread(new Thread1()).start();
  7. new Thread(new Thread2()).start();
  8. }
  9. }
  10. class Thread1 implements Runnable{
  11. @Override
  12. public void run() {
  13. synchronized (Demo07_DeadLock.lock1) {
  14. System.out.println("取得第一把锁之后要做的事情");
  15. try {
  16. Thread.sleep(100);
  17. } catch (InterruptedException e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. synchronized (Demo07_DeadLock.lock2) {
  22. System.out.println("Thread1同时取得两把锁之后要做的事情");
  23. }
  24. }
  25. }
  26. }
  27. class Thread2 implements Runnable{
  28. @Override
  29. public void run() {
  30. synchronized (Demo07_DeadLock.lock1) {
  31. synchronized (Demo07_DeadLock.lock2) {
  32. System.out.println("取得第二把锁之后要做的事情");
  33. System.out.println("Thread2同时取得两把锁之后要做的事情");
  34. }
  35. }
  36. }
  37. }

17,线程组ThreadGroup 默认处于同一个组里面
使用线程组可以统一设置这个组内线程的一些东西。比如设置优先级,设置是否是守护线程。

  1. package com.sikiedu.chapter1;
  2. public class MyRunnable implements Runnable {
  3. @Override
  4. public void run() {
  5. for(int i=0;i<100;i++) {
  6. Thread t = Thread.currentThread();
  7. System.out.println(t.getName()+":"+i);
  8. }
  9. }
  10. }
  1. package com.sikiedu.chapter1;
  2. public class Demo08_ThreadGroup {
  3. public static void main(String[] args) {
  4. MyRunnable r = new MyRunnable();
  5. ThreadGroup tg = new ThreadGroup("我们的线程组");
  6. Thread t1 = new Thread(tg,r);
  7. Thread t2 = new Thread(tg,r);
  8. System.out.println(tg.getName());//我们的线程组
  9. //批量设置线程属性
  10. t1.setDaemon(true);
  11. t1.setPriority(9);
  12. t1.interrupt();
  13. t1.start();
  14. t2.start();
  15. }
  16. static void DefalutThreadGroup()
  17. {
  18. // 不设置线程组的话默认处于同一个组里面
  19. MyRunnable r = new MyRunnable();
  20. Thread t1 = new Thread(r);
  21. Thread t2 = new Thread(r);
  22. ThreadGroup tg = t1.getThreadGroup();
  23. System.out.println(tg.getName());//main
  24. System.out.println(t2.getThreadGroup().getName());//main
  25. t1.start();
  26. t2.start();
  27. }
  28. }

18,定时器(本质也是线程)
作用:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
使用类:Timer和TimerTask

方法
timer.schedule(TimerTask task, long delay)
timer.schedule(TimerTask task, long delay, long period)
timer.schedule(TimerTask task, Date time)
timer.cancel();

  1. package com.sikiedu.chapter1;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. public class Demo09_Timer {
  5. public static void main(String[] args) {
  6. Timer t = new Timer();
  7. // t.schedule(new MyTimerTask(), 2000);//等待2s,执行任务
  8. // t.schedule(new MyTimerTask(), 2000, 3000);//等待2s,然后每隔3s执行一次
  9. // t.cancel();//终止任务
  10. }
  11. }
  12. class MyTimerTask extends TimerTask{
  13. @Override
  14. public void run() {
  15. System.out.println("定时器任务");
  16. }
  17. }

网络编程(两个计算机通信)

  1. 简述:跟网络相关的编程<br /> 例子: 下载资源<br /> 资源上传<br /> 从服务器(某个网络终端)取得数据<br /> 向其他计算机发送消息<br /> 接收其他计算机发送的消息<br /> 计算机之间的交流,计算机之间数据的传递

1,什么是ip地址
简述:ip地址是网络中计算机的唯一标识。
举例:xx.xx.xx.xx xx是0-255之间的一个数字
问题:
什么是局域网ip(192.168.xx.xx),什么是外网ip?
如何查看本机ip?(ipconfig)
ip是不能重复的
局域网ip在局域网内,不可以重复。外网ip在外网环境下不可以重复

每个域名,访问的其实都是某个服务器,服务器其实是一台计算机,是计算机就有一个唯一的ip地址。
访问域名的时候,其实就是通过服务器的ip地址,找到服务器并获取数据。
域名跟ip地址是一一对应的。

当前计算机与ping的地址之间的通信是否通畅。
ping 域名
ping ip地址

特殊的ip地址
本机 127.0.0.1 localhost

什么是ipv4和ipv6
由于全球计算机数量越来越多,ipv6的出现是为了解决ipv4可能不够用的问题,有6位,每一部分由16进制组成。了解就行。

2,既然要在Java里面做网络编程,那在Java里面肯定要处理IP地址。
在Java程序里面如何表示一个IP地址呢? 通过字符串?还是某个系统的类?
a)InetAddress表示一个IP地址对象
//可以根据主机名(计算机名)构建InetAddress对象
//也可以根据ip地址(字符串)构建InetAddress对象
InetAddress.getByName(String name);

  1. package com.sikiedu.chapter2;
  2. import java.net.InetAddress;
  3. import java.net.UnknownHostException;
  4. public class Demo01_InetAddress {
  5. public static void main(String[] args) throws Exception {
  6. InetAddress ip1 = InetAddress.getByName("DESKTOP-J8UV13P");//此电脑->右键->设备名称
  7. InetAddress ip2 = InetAddress.getByName("192.168.31.225");
  8. System.out.println(ip1);// DESKTOP-J8UV13P/192.168.31.225
  9. System.out.println(ip2);// /192.168.31.225
  10. }
  11. }

3,端口号
有了地址可以找到住的地方,这个地方可能有很多人同时居住,我们还需要一个收件人。端口就相当于收件人。
我们的电脑上有很多运行的程序,有的程序不需要跟外界交互(单机软件)
有的程序需要跟外界交互,这个时候我们需要通过端口号区分我们要跟那个软件交互。

总结:通过ip定位计算机,通过port定位哪个软件
注意:端口号是不能重复的。端口号是一个数字。
百度百科:https://baike.baidu.com/item/%E7%AB%AF%E5%8F%A3%E5%8F%B7/10883658

4,通信协议(两个计算机通信,也就是两个计算机交流,也就是两个计算机交流数据)
通信规则
UDP
速度快
不需要建立连接
不可靠
TCP
速度慢
需要通过三次握手建立连接
可靠
举例:
UDP:发短信
TCP:打电话

网络编程的三要素总结:
IP地址,端口号,通信协议。

5,Socket套接字
在程序中我们通过Socket进行通信,在使用Socket通信的时候
需要指定上面所说的几个条件(ip,port,协议)

数据发送分成两步
第一步是监听(等待数据发送过来),用来接收数据。需要指定监听的端口号
第二步是发送,需要指定发送到哪个计算机(IP地址),需要指定发送到这个计算机的哪个端口。


Socket中分为发送端和接收端
发送端一般为客户端,接收端为服务器端。
一般情况下我们会有多个客户端,一个服务器端。

6,使用UDP发送和接收数据,并实现数据的循环接收和循环发送

  1. package com.sikiedu.chapter2;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4. import java.net.InetAddress;
  5. import java.util.Scanner;
  6. public class Demo02_UDP_Send {
  7. public static void main(String[] args) throws Exception {
  8. DatagramSocket ds = new DatagramSocket();
  9. Scanner s = new Scanner(System.in);
  10. while(true) {
  11. String str = s.nextLine();
  12. if(str.equals("end"))break;
  13. byte[] buf = str.getBytes();
  14. int length = buf.length;
  15. InetAddress address= InetAddress.getByName("192.168.31.225");//127.0.0.1 localhost
  16. int port = 7879;
  17. DatagramPacket dp = new DatagramPacket(buf, length, address, port);
  18. ds.send(dp);
  19. }
  20. ds.close();//释放资源
  21. }
  22. }
  1. package com.sikiedu.chapter2;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4. import java.net.InetAddress;
  5. public class Demo03_UDP_Receive {
  6. public static void main(String[] args) throws Exception {
  7. DatagramSocket ds = new DatagramSocket(7879);//要指定 监听哪个端口号来接收数据
  8. while(true) {
  9. byte[] buf = new byte[1024];
  10. int length = buf.length;
  11. DatagramPacket dp = new DatagramPacket(buf, length);
  12. ds.receive(dp);//程序会在这里等待,等待数据的到来
  13. String str = new String( dp.getData(),0,dp.getLength() );
  14. InetAddress address = dp.getAddress();//发送端的地址
  15. // System.out.println(dp.getPort());//发送端的端口号,并不是接收端的7879端口,由系统自动分配,每次都不同,一般没啥作用
  16. System.out.println(address+"----"+str);
  17. }
  18. // ds.close();接收数据端需要不断监听,所以不需要关闭
  19. }
  20. }

7,实现双向聊天功能

  1. package com.sikiedu.chapter2;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4. import java.net.InetAddress;
  5. public class ReceiveThread extends Thread {
  6. private int port;
  7. public ReceiveThread(int port) {
  8. this.port=port;
  9. }
  10. @Override
  11. public void run() {
  12. DatagramSocket ds=null;
  13. try {
  14. ds = new DatagramSocket(port);
  15. while(true) {
  16. byte[] buf = new byte[1024];
  17. int length = buf.length;
  18. DatagramPacket dp = new DatagramPacket(buf, length);
  19. ds.receive(dp);//程序会在这里等待,等待数据的到来
  20. String str = new String( dp.getData(),0,dp.getLength() );
  21. InetAddress address = dp.getAddress();
  22. // System.out.println(dp.getPort());
  23. System.out.println(address+":"+str);
  24. }
  25. } catch (Exception e) {
  26. // TODO Auto-generated catch block
  27. e.printStackTrace();
  28. } finally {
  29. if(ds!=null)
  30. ds.close();
  31. }
  32. }
  33. }
  1. package com.sikiedu.chapter2;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4. import java.net.InetAddress;
  5. import java.util.Scanner;
  6. public class SendThread extends Thread {
  7. private int port;
  8. public SendThread(int port) {
  9. this.port = port;
  10. }
  11. @Override
  12. public void run() {
  13. DatagramSocket ds=null;
  14. try {
  15. ds = new DatagramSocket();
  16. Scanner s = new Scanner(System.in);
  17. while(true) {
  18. String str = s.nextLine();
  19. if(str.equals("end"))break;
  20. byte[] buf = str.getBytes();
  21. int length = buf.length;
  22. InetAddress address= InetAddress.getByName("192.168.31.225");//127.0.0.1 localhost
  23. DatagramPacket dp = new DatagramPacket(buf, length, address, port);
  24. ds.send(dp);
  25. }
  26. } catch (Exception e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. } finally {
  30. if(ds!=null)
  31. ds.close();
  32. }
  33. }
  34. }
  1. package com.sikiedu.chapter2;
  2. public class Demo04_Chat_User01 {
  3. public static void main(String[] args) {
  4. new SendThread(7878).start();
  5. new ReceiveThread(7879).start();
  6. }
  7. }
  1. package com.sikiedu.chapter2;
  2. public class Demo05_Chat_User02 {
  3. public static void main(String[] args) {
  4. new ReceiveThread(7878).start();
  5. new SendThread(7879).start();
  6. }
  7. }

8,TCP发送端(客户端)
客户端和服务器端双方发送数据正常。
但是会出现问题:
1、在客户端输入end关闭时客户端会抛出异常Socket closed。明明在接收时通过if(s.isClosed())break;判断了,为什么还会出现这种情况呢?
答:因为线程是异步的,客户端第一次运行接收线程时isClosed()为false,然后执行到input.read(buf);一直等待服务器发送数据,收到数据后再次进行等待,会一直停留在input.read(buf);这行语句。而在这时我们客户端Socket关闭连接了,而input是依赖Socket的,输入流也会关闭。
解决方案:抛出Exception异常,不影响执行。
2、bug:服务器端输入end无效。
TODO

  1. package com.sikiedu.chapter2;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. import java.net.UnknownHostException;
  7. import java.util.Scanner;
  8. //使用tcp 发送端:client客户端 接收端server服务器端
  9. public class Demo06_TCP_Send {
  10. public static void main(String[] args) throws Exception {
  11. //Socket
  12. //TCP需要建立连接
  13. Socket s = new Socket("192.168.31.225", 7878);//建立跟指定ip port建立连接
  14. InputStream input = s.getInputStream();
  15. OutputStream output = s.getOutputStream();//得到输出流。使用流适合发送大容量的数据,数据大小不受限制,可以持续写数据,并保证稳定性
  16. //客户端接收数据。
  17. //如果不使用多线程的话。执行到while(true)时就永远不会执行下面的发送数据代码了,所以需要使用多线程并行
  18. new Thread() {
  19. public void run() {
  20. int length = -1;
  21. byte[] buf = new byte[1024];
  22. try {
  23. while(true) {
  24. if(s.isClosed())break;
  25. length = input.read(buf);
  26. if(length==-1)break;
  27. System.out.println(new String(buf,0,length));
  28. }
  29. } catch (Exception e) {
  30. // TODO Auto-generated catch block
  31. e.printStackTrace();
  32. }
  33. }
  34. }.start();
  35. //客户端发送数据
  36. Scanner scanner = new Scanner(System.in);
  37. while(true) {
  38. String str = scanner.nextLine();
  39. if(str.equals("end"))break;
  40. //输出流写入数据
  41. output.write(str.getBytes());
  42. }
  43. s.close();
  44. }
  45. }
  1. package com.sikiedu.chapter2;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. import java.util.Scanner;
  8. public class Demo07_TCP_Receive {
  9. public static void main(String[] args) throws Exception {
  10. ServerSocket ss = new ServerSocket(7878);
  11. Socket client = ss.accept();//接收,建立跟发送端的连接。会等待客户端的连接
  12. System.out.println("接收到一个客户端的链接");
  13. //new Socket("192.168.0.107", 7878);发送端执行这行的时候,会发送跟接收端建立连接的请求
  14. InputStream input = client.getInputStream();//接收使用输入流
  15. OutputStream output = client.getOutputStream();//发送使用输出流
  16. //服务器端发送数据。
  17. //如果不使用多线程的话。执行到while(true)时就永远不会执行下面的发送数据代码了,所以需要使用多线程并行
  18. Thread t = new Thread() {
  19. public void run() {
  20. Scanner scanner = new Scanner(System.in);
  21. while(true) {
  22. if(client.isClosed())break;
  23. String str = scanner.nextLine();
  24. if(str.equals("end"))break;
  25. try {
  26. output.write(str.getBytes());
  27. } catch (IOException e) {
  28. // TODO Auto-generated catch block
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. };
  34. t.setDaemon(true);//设置成守护线程。防止客户端关闭连接时,服务器端卡在scanner.nextLine();//然而并无卵用,TODO
  35. t.start();
  36. //服务器端接收数据
  37. byte[] buf = new byte[1024];
  38. // while(true) {//接收数据不能使用死循环,因为客户端关闭连接时接收到的数据length是-1,需用下面这种方法
  39. // int length = input.read(buf);
  40. // System.out.println(length);
  41. // System.out.println(new String(buf,0,length));
  42. // }
  43. int length = -1;//-1代表流结束,客户端关闭连接close()时会返回-1
  44. while( (length = input.read(buf))!=-1 ) {//read()会等待客户端发送数据
  45. System.out.println(new String(buf,0,length));
  46. }
  47. client.close();
  48. ss.close();
  49. }
  50. }

10,聊天室功能
一个服务器端接收数据并输出,多个客户端可以持续的发送数据
一个服务器端接收数据并转发到所有客户端。

  1. package com.sikiedu.chapter2;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. public class Demo08_TCP_Server {
  7. public static void main(String[] args) {
  8. new ConnectionThread().start();
  9. }
  10. }
  11. class ConnectionThread extends Thread {
  12. @Override
  13. public void run() {
  14. ServerSocket ss = null;
  15. try {
  16. ss = new ServerSocket(7878);
  17. while(true) {
  18. Socket s = ss.accept();//监听客户端的连接请求
  19. System.out.println("接收到一个客户端连接:"+s.getInetAddress());
  20. new ClientThread(s).start();//必须开启线程,不然会阻塞下一个客户端请求
  21. }
  22. } catch (IOException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. } finally {
  26. try {
  27. if(ss!=null)
  28. ss.close();
  29. } catch (IOException e) {
  30. // TODO Auto-generated catch block
  31. e.printStackTrace();
  32. }
  33. }
  34. }
  35. }
  36. class ClientThread extends Thread{
  37. private Socket s;
  38. public ClientThread(Socket s) {
  39. this.s= s;
  40. }
  41. @Override
  42. public void run() {
  43. try {
  44. InputStream input= s.getInputStream();
  45. byte[] buf = new byte[1024];
  46. int length = -1;
  47. while( (length = input.read(buf))!=-1 ) {
  48. System.out.println(new String(buf,0,length));
  49. }
  50. } catch (IOException e) {
  51. // TODO Auto-generated catch block
  52. e.printStackTrace();
  53. } finally {
  54. if(s!=null)
  55. try {
  56. s.close();
  57. } catch (IOException e) {
  58. // TODO Auto-generated catch block
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  1. package com.sikiedu.chapter2;
  2. import java.io.IOException;
  3. import java.net.Socket;
  4. import java.net.UnknownHostException;
  5. import java.util.Scanner;
  6. public class Demo09_TCP_Client {
  7. public static void main(String[] args) throws Exception {
  8. Socket s = new Socket("192.168.31.225", 7878);
  9. Scanner scanner = new Scanner(System.in);
  10. s.getOutputStream().write(scanner.nextLine().getBytes());
  11. s.close();
  12. }
  13. }

枚举类型

  1. 作用:见名知意,增加代码可读性。<br /> <br /> <br /> 注意枚举类型的作用是见名知意,它底层还是使用int类型来存储和判断的。<br /> 枚举类型里面的每个值都有一个对应的int值。<br />
  1. package com.sikiedu.chapter3;
  2. public class Demo01_CustomEnum {
  3. public static void main(String[] args) {
  4. RoleTypeEnum rt1 = RoleTypeEnum.TEACHER;
  5. RoleTypeEnum rt2 = RoleTypeEnum.STUDENT;
  6. if(rt1==RoleTypeEnum.PRINCIPAL) {//因为本质是int,所以可以直接==比较
  7. }
  8. SeasonEnum s1 = SeasonEnum.FALL;
  9. SeasonEnum s2= SeasonEnum.WINTER;
  10. System.out.println(s1);//FALL。直接输出SeasonEnum对象输出的是枚举所对应的字符串
  11. System.out.println(s1.ordinal());//2。获取枚举值对应的int
  12. for(SeasonEnum se : SeasonEnum.values()) {//遍历枚举类型的所有值
  13. System.out.println(se);//SPRINT SUMMER FALL WINTER
  14. }
  15. }
  16. }
  17. public enum RoleTypeEnum {
  18. TEACHER,STUDENT,PRINCIPAL,HEADTEACHER,LOGISTICS
  19. }
  20. public enum SeasonEnum {
  21. SPRINT,SUMMER,FALL,WINTER
  22. }

类的加载机制和反射

类的使用分为三个步骤:
类的加载->类的连接->类的初始化
1,类的加载
当程序运行的时候,系统(JVM)会首先把我们要使用的Java类加载到内存中。这里加载的是编译后的.class文件。
每个类加载到内存中,会创建一个对应的Class对象。这个Class对象保存了这个类有哪些成员(数据成员,方法成员)。
注意:这里只有在某个Java类被使用的时候,才会被加载。
加载时机:任何用到这个类的时候。(实例化,使用里面的静态静态成员….)

2,类加载器(JVM里面的一个东东)
作用:将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并生成与之对应的java.lang.Class对象。
分类:
Bootstrap ClassLoader 根类加载器
加载JRE中的核心类,比如java.lang.String 。。。
Extension ClassLoader 扩展类加载器
加载JRE中的扩展类
System ClassLoader 系统类加载器
一般用来加载我们自己写的类

3,反射
解释:在程序运行的时候,查看一个类有哪些信息(包含的数据成员和方法成员)。这个过程称之为反射。

程序中用到了java.lang.Class对象,那就是在用反射。

如果我们知道我们要使用哪个类,那么我们只需要只用这个对应的类创建对象,然后就可以调用获取这个对象里面的数据和调用里面的方法。
(知道类,知道这个类里面有哪些属性和方法——>使用这个对象里面对应的属性和方法)
但是我们不知道我们要使用的是哪个类,这个时候我们需要使用反射获取到类的信息,里面有哪些成员,再使用。
(不知道——>获取类信息—->使用(实例化对象,调用属性和方法))反射

4,获取Class对象(反射的第一步)
方式一:通过对象获得 对象.getClass()
方式二(很少用):通过类获得 类.class
方式三:Class.forName(“类路径”)//必须传递完整路径(加上包名)

  1. package com.sikiedu.chapter4;
  2. public class Demo01_GetClass {
  3. public static void main(String[] args) throws Exception {
  4. //获取Class对象
  5. //1,通过对象
  6. User user1 = new User(100,"siki","123");
  7. User user2 = new User(200,"edu","456");
  8. Class c1 = user1.getClass();
  9. System.out.println(c1);//class com.sikiedu.chapter4.User
  10. Class c2 = user2.getClass();
  11. System.out.println(c2);//class com.sikiedu.chapter4.User
  12. System.out.println(c1==c2);//true 每个类只有对应的一个Class对象
  13. Demo01_GetClass o = new Demo01_GetClass();
  14. Class c3 = o.getClass();
  15. System.out.println(c3==c2);//false c2是User的Class对象,c3是Demo01_GetClass的Class对象
  16. //2,通过类(很少用)
  17. Class c4 = User.class;
  18. System.out.println(c4==c1);//true
  19. //3,Class.forName
  20. Class c5 = Class.forName("java.lang.Math");
  21. System.out.println(c5==c1);//false
  22. Class c6 = Class.forName("com.sikiedu.chapter4.User");
  23. System.out.println(c6==c1);//true
  24. }
  25. }

5,从Class对象中获取信息
测试类User:

  1. package com.sikiedu.chapter4;
  2. public class User {
  3. private int id;
  4. private String username;
  5. private String password;
  6. public int age;
  7. private User(int id) {
  8. this.id = id;
  9. }
  10. public User() {}
  11. public User(int id,String username,String password) {
  12. this.id = id;
  13. this.username = username;
  14. this.password = password;
  15. }
  16. public User(int id ,String username,String password,int age) {
  17. this.id = id;
  18. this.username = username;
  19. this.password = password;
  20. this.age = age;
  21. }
  22. public void show() {
  23. System.out.println(id+":"+username+":"+password);
  24. }
  25. public void study() {
  26. System.out.println("学习");
  27. }
  28. public void study(String courseName) {
  29. System.out.println(username+"正在学习"+courseName);
  30. }
  31. private void shower(String name) {
  32. System.out.println(username+"正在使用"+name+"洗澡");
  33. }
  34. }

5.1,得到构造方法:
getConstructors();//获取所有public构造方法信息
getConstructor(Class<?> … parameterTypes );//根据参数类型,获取指定参数类型的public构造方法
getDeclaredConstructor(Class<?> … parameterTypes );//忽略访问权限,获取构造方法
getDeclaredConstructors();//忽略访问权限,获取构造方法

  1. package com.sikiedu.chapter4;
  2. import java.lang.reflect.Constructor;
  3. public class Demo02_GetConstructorByClass {
  4. public static Class c;
  5. public static void main(String[] args) throws Exception {
  6. c = Class.forName("com.sikiedu.chapter4.User");
  7. // getConstructors();
  8. // getConstructor();
  9. // getConstructorByPara();
  10. // getAllConstructors();
  11. // getSpecifiedConstructorByPara();
  12. }
  13. // 得到所有的public构造方法
  14. static void getConstructors() {
  15. Constructor[] cs = c.getConstructors();//
  16. for (Constructor con : cs) {
  17. System.out.println(con);
  18. }
  19. /*
  20. * 输出: public com.sikiedu.chapter4.User() public
  21. * com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String) public
  22. * com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String,int)
  23. */
  24. }
  25. // 得到指定的public构造方法,并调用构造方法
  26. // 1,得到默认的构造方法并调用
  27. static void getConstructor() throws Exception {
  28. Constructor con = c.getConstructor();
  29. System.out.println(con);// public com.sikiedu.chapter4.User()
  30. Object o1 = con.newInstance();
  31. User user = (User) o1;
  32. user.show();// 0:null:null
  33. }
  34. // 2,得到带参数的构造方法并调用
  35. static void getConstructorByPara() throws Exception {
  36. Constructor con = c.getConstructor(int.class, String.class, String.class);
  37. System.out.println(con);// public com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String)
  38. Object o1 = con.newInstance(100, "siki", "123");
  39. User user = (User) o1;
  40. user.show();// 100:siki:123
  41. }
  42. // 忽略访问权限,得到所有的构造函数(public + private)
  43. static void getAllConstructors() {
  44. Constructor[] cs = c.getDeclaredConstructors();
  45. for (Constructor con : cs) {
  46. System.out.println(con);
  47. }
  48. /*
  49. * 输出 public
  50. * com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String,int) public
  51. * com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String) public
  52. * com.sikiedu.chapter4.User() private com.sikiedu.chapter4.User(int)
  53. */
  54. }
  55. // 忽略访问权限,得到带参数的构造方法并调用(public + private)
  56. static void getSpecifiedConstructorByPara() throws Exception {
  57. Constructor con = c.getDeclaredConstructor(int.class);
  58. System.out.println(con);// private com.sikiedu.chapter4.User(int)
  59. con.setAccessible(true);// 这里必须加这句才能调用private的构造方法
  60. Object o1 = con.newInstance(100);
  61. User user = (User) o1;
  62. user.show(); // 100:null:null
  63. }
  64. }

5.2,得到成员变量
getFields
getDeclaredFields
getField
getDeclaredField

  1. package com.sikiedu.chapter4;
  2. import java.lang.reflect.Field;
  3. //构造方法 成员变量 成员方法
  4. public class Demo03_GetFieldByClass {
  5. public static void main(String[] args) throws Exception {
  6. User u = new User(100,"siki","123",40);
  7. Class c = Class.forName("com.sikiedu.chapter4.User");
  8. // //得到所有的public字段(成员变量)
  9. // Field[] fields = c.getFields();
  10. // for( Field f : fields ) {
  11. // System.out.println(f);//public int com.sikiedu.chapter4.User.age
  12. // }
  13. // //得到指定的public字段(成员变量)
  14. // Field ageField = c.getField("age");
  15. // System.out.println(ageField);//public int com.sikiedu.chapter4.User.age
  16. // int age = ageField.getInt(u);
  17. // System.out.println(age);//40 通过反射的方式访问成员变量 正常情况:u.age 反射情况:age.u
  18. // //忽略访问权限,得到所有的字段(public + private)
  19. // Field[] fields = c.getDeclaredFields();
  20. // for( Field f : fields ) {
  21. // System.out.println(f);
  22. // }
  23. // /*
  24. // * private int com.sikiedu.chapter4.User.id
  25. // private java.lang.String com.sikiedu.chapter4.User.username
  26. // private java.lang.String com.sikiedu.chapter4.User.password
  27. // public int com.sikiedu.chapter4.User.age
  28. //
  29. // * */
  30. // //忽略访问权限,得到制定的字段(public + private)
  31. // Field usernameField = c.getDeclaredField("username");
  32. // usernameField.setAccessible(true);
  33. // System.out.println(usernameField.get(u));//siki
  34. }
  35. }

5.3,得到成员方法
getMethod
getMethods
getDeclaredMethod
getDeclaredMethods

  1. package com.sikiedu.chapter4;
  2. import java.lang.reflect.Method;
  3. public class Demo04_GetMethodByClass {
  4. public static void main(String[] args) throws Exception {
  5. User u = new User(100,"siki","123",40);
  6. Class c = Class.forName("com.sikiedu.chapter4.User");
  7. // //得到所有public方法,包括父类的
  8. // Method[] methods = c.getMethods();
  9. // for(Method m :methods) {
  10. // System.out.println(m);
  11. // }
  12. // /*
  13. // * public void com.sikiedu.chapter4.User.show()
  14. // public void com.sikiedu.chapter4.User.study(java.lang.String)
  15. // public void com.sikiedu.chapter4.User.study()
  16. // public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
  17. // public final void java.lang.Object.wait() throws java.lang.InterruptedException
  18. // public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
  19. // public boolean java.lang.Object.equals(java.lang.Object)
  20. // public java.lang.String java.lang.Object.toString()
  21. // public native int java.lang.Object.hashCode()
  22. // public final native java.lang.Class java.lang.Object.getClass()
  23. // public final native void java.lang.Object.notify()
  24. // public final native void java.lang.Object.notifyAll()
  25. // * */
  26. //
  27. // //得到指定的public方法
  28. // //u.show() show.u
  29. // Method m= c.getMethod("show");
  30. // Object o =m.invoke(u);//100:siki:123
  31. // System.out.println(o);//null
  32. // Method studyMethod = c.getMethod("study", String.class);
  33. // studyMethod.invoke(u, "生物");//siki正在学习生物
  34. // //忽略访问权限,得到所有的自身(不包括父类)方法(public + private)
  35. // Method[] methods = c.getDeclaredMethods();//只得到自身的所有方法(包括private),不包括父类里面的方法
  36. // for(Method m :methods) {
  37. // System.out.println(m);
  38. // }
  39. // /*
  40. // * private void com.sikiedu.chapter4.User.shower(java.lang.String)
  41. // public void com.sikiedu.chapter4.User.study()
  42. // public void com.sikiedu.chapter4.User.study(java.lang.String)
  43. // public void com.sikiedu.chapter4.User.show()
  44. // * */
  45. // //忽略访问权限,得到指定的自身(不包括父类)方法(public + private)
  46. // Method showerM = c.getDeclaredMethod("shower", String.class);
  47. // showerM.setAccessible(true);
  48. // showerM.invoke(u, "海飞丝");//siki正在使用海飞丝洗澡
  49. }
  50. }

Constructor成员:
newInstance
setAccessible