多线程
正常来说代码执行顺序从上到下
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方法
public class MyThread extends Thread {public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for(int i=0;i<100;i++) {if(interrupted()) {//当前线程被设置为中断状态//做一些事情System.out.println("释放资源");break;}System.out.println(getName()+":" +i);}}}
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
Thread mainThread = Thread.currentThread();mainThread.setPriority(10);MyThread thread = new MyThread();System.out.println(mainThread.getPriority());System.out.println(thread.getPriority());
// thread.run();//error 相当于普通方法调用 thread.start();//启动线程
int j = 0;for(int i=0;i<10000000;i++) {j+=i;}System.out.println("MainThread"+System.currentTimeMillis());}
}
```javapackage com.sikiedu.chapter1;public class Demo02_Thread {public static void main(String[] args) throws Exception {// try {// Thread.sleep(4000);// } catch (InterruptedException e) {// // TODO Auto-generated catch block// e.printStackTrace();// }MyThread t1 = new MyThread("线程1");// System.out.println("ThreadT1Name:"+t1.getName());t1.start();Thread.sleep(1);// t1.stop();t1.interrupt();//// t1.join();//当t1加入到当前线程中,就是加入到主线程中,就是放到主线程中去继续执行//t1.start();//不可以重复调用startMyThread t2 = new MyThread("线程2");// System.out.println("ThreadT2Name:"+t2.getName());// t2.setDaemon(true);//需要再启动线程前设置守护线程t2.start();System.out.println("Main end");}}
7,线程生命周期:https://blog.csdn.net/lonelyroamer/article/details/7949969
8,创建线程(方法二)常用
a)实现Runnable接口
b)实现run方法
c)创建当前MyRunnable类的对象和Thread的对象,并使用thread对象启动线程
package com.sikiedu.chapter1;public class MyRunnable implements Runnable {private String data = "";@Overridepublic void run() {for(int i=0;i<100;i++) {Thread t = Thread.currentThread();System.out.println(t.getName()+":"+i);}}}
package com.sikiedu.chapter1;// extends Thread// implements Runnablepublic class Demo03_CreateThreadMethod2 {public static void main(String[] args) {MyRunnable t = new MyRunnable();Thread t1 = new Thread(t,"线程1");t1.start();Thread t2 = new Thread(t,"线程2");t2.start();}}
其他<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(){}
package com.sikiedu.chapter1;public class Demo04_CreateThread {public static void main(String[] args) {// // 匿名内部类// Runnable r = new Runnable() {//// @Override// public void run() {// for(int i = 0;i<100;i++) {// System.out.println(Thread.currentThread().getName()+":"+i);// }// }//// };////// Thread t = new Thread(r,"匿名内部类线程");//// t.start();// new Thread(r,"匿名内部类线程").start();new Thread() {@Overridepublic void run() {for(int i = 0;i<100;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}.start();}}
11,我们用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。
方法一:
package com.sikiedu.chapter1;public class TicketThread extends Thread{private static int count = 100;//所有线程应该一共有100张票,而不是每个售票点对象都有100张票,所以票数应设置成staticpublic TicketThread(String name) {super(name);}@Overridepublic void run() {while(true) {if(count>0) {System.out.println(getName()+ "卖出第"+count+"张票");count--;}else {break;}}}}
TicketThread t1 = new TicketThread("售票点1");TicketThread t2 = new TicketThread("售票点2");TicketThread t3 = new TicketThread("售票点3");TicketThread t4 = new TicketThread("售票点4");t1.start();t2.start();t3.start();t4.start();
问题:count即使设置成static后依然会出现重复卖同一张票的情况。
方法二:
package com.sikiedu.chapter1;public class TicketRunnable implements Runnable {private int count= 100;@Overridepublic void run() {while(count>0) {if(count>0) {//线程1 线程2System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100count--;}else {break;}}}}
TicketRunnable t = new TicketRunnable();Thread t1 = new Thread(t,"售票点1");Thread t2 = new Thread(t,"售票点2");Thread t3 = new Thread(t,"售票点3");Thread t4 = new Thread(t,"售票点4");t1.start();t2.start();t3.start();t4.start();
问题:count即使设置成static后依然会出现重复卖同一张票的情况。
12,线程安全问题
售票问题原因:多个线程同时要修改一个变量的时候,引起冲突
线程安全问题解决:
synchronized(对象){//锁住某个对象,如果这个对象已经被锁定,那么等待。
}
package com.sikiedu.chapter1;public class TicketThread extends Thread{private static int count = 100;//所有线程应该一共有100张票,而不是每个售票点对象都有100张票,所以票数应设置成staticprivate static Object lock = new Object();public TicketThread(String name) {super(name);}@Overridepublic void run() {while(true) {synchronized (lock) {//这里lock不可以换成this,因为每个this各代表一个对象if(count>0) {System.out.println(getName()+ "卖出第"+count+"张票");count--;}else {break;}}}}}
package com.sikiedu.chapter1;public class TicketRunnable implements Runnable {private int count= 100;private Object lock = new Object();@Overridepublic void run() {while(true) {//这里lock可以换成thissynchronized (lock) {//第一个线程来的 时候会锁上,并拿走钥匙,第二个线程来的 时候,发现被锁上了,等待if(count>0) {//线程1 线程2System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100count--;}else {break;}}//执行完代码,归还钥匙try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
a)出现线程安全问题的地方,要锁同一个对象(可以是当前对象this,也可以单独创建一个Object对象lock,只限方法二)
b)锁住某个对象,如果这个对象已经被锁定,那么停止当前线程的执行,一直等待,一直等到对象被解锁。
(保证同一个时间,只有一个线程在使用这个对象,)
c)创建同步方法
去掉synchronized (this),把方法声明为synchronized
同步方法锁的是哪个对象呢?锁定的是当前对象this
package com.sikiedu.chapter1;public class TicketRunnable implements Runnable {private int count= 100;private Object lock = new Object();@Overridepublic void run() {while(count>0) {sellTicket();try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public synchronized void sellTicket() {// synchronized (this) {//第一个线程来的 时候会锁上,并拿走钥匙,第二个线程来的 时候,发现被锁上了,等待if(count>0) {//线程1 线程2System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100count--;}// }//执行完代码,归还钥匙}}
13,线程安全的类
安全: StringBuffer(查看源码会发现方法声明为synchronized。所以线程安全,不过效率略低。其他类同理) Vector
不安全:StringBuilder ArrayList
14,同步锁的第二种使用方式
a)创建锁对象 ReentrantLock lock = new ReentrantLock();
b)加锁和解锁
使用try finally
lock.lock();加锁
lock.unlock();解锁
package com.sikiedu.chapter1;import java.util.concurrent.locks.ReentrantLock;public class TicketRunnable implements Runnable {private int count= 100;private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while(count>0) {lock.lock();//加锁try {if(count>0) {//线程1 线程2System.out.println(Thread.currentThread().getName()+ "卖出第"+count+"张票");//线程1 100count--;}} finally {//把unlock()解锁放在finally里,保证即使需要加锁的代码中出现错误也能执行到unlock()lock.unlock();}try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
15,线程安全问题的第二个例子
买票问题升级(两种卖票方式,一种通过电影院窗口,一种通过手机App)
问:之前是在一个Runnable对象中进行线程安全的处理。现在是两个Runnable对象,那该怎么保证是同一个锁呢?
答:定义一个成员变量,通过构造方法把锁对象传入即可
package com.sikiedu.chapter1;public class TicketMng {public static int count = 100;}
package com.sikiedu.chapter1;public class WindowThread implements Runnable {public WindowThread(Object lock) {this.lock=lock;}private Object lock;@Overridepublic void run() {while( TicketMng.count>0 ) {synchronized (lock) {if(TicketMng.count>0) {System.out.println(Thread.currentThread().getName()+"售出第"+TicketMng.count+"张票");TicketMng.count--;}}try {Thread.sleep(2);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
package com.sikiedu.chapter1;public class AppThread implements Runnable {public AppThread(Object lock) {this.lock=lock;}private Object lock;@Overridepublic void run() {while( TicketMng.count>0 ) {synchronized (lock) {if(TicketMng.count>0) {System.out.println(Thread.currentThread().getName()+"售出第"+TicketMng.count+"张票");TicketMng.count--;}}try {Thread.sleep(1);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
package com.sikiedu.chapter1;public class Demo06_Practice {public static void main(String[] args) {Object lock = new Object();WindowThread windowThread = new WindowThread(lock);AppThread appThread = new AppThread(lock);new Thread(windowThread,"窗口").start();new Thread(appThread,"手机App").start();}}
16,死锁问题
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
package com.sikiedu.chapter1;public class Demo07_DeadLock {public static Object lock1 = new Object();public static Object lock2 = new Object();public static void main(String[] args) {new Thread(new Thread1()).start();new Thread(new Thread2()).start();}}class Thread1 implements Runnable{@Overridepublic void run() {synchronized (Demo07_DeadLock.lock1) {System.out.println("取得第一把锁之后要做的事情");try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (Demo07_DeadLock.lock2) {System.out.println("Thread1同时取得两把锁之后要做的事情");}}}}class Thread2 implements Runnable{@Overridepublic void run() {synchronized (Demo07_DeadLock.lock2) {System.out.println("取得第二把锁之后要做的事情");try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (Demo07_DeadLock.lock1) {System.out.println("Thread2同时取得两把锁之后要做的事情");}}}}
解决方法:锁的顺序设置相同
package com.sikiedu.chapter1;public class Demo07_DeadLock {public static Object lock1 = new Object();public static Object lock2 = new Object();public static void main(String[] args) {new Thread(new Thread1()).start();new Thread(new Thread2()).start();}}class Thread1 implements Runnable{@Overridepublic void run() {synchronized (Demo07_DeadLock.lock1) {System.out.println("取得第一把锁之后要做的事情");try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (Demo07_DeadLock.lock2) {System.out.println("Thread1同时取得两把锁之后要做的事情");}}}}class Thread2 implements Runnable{@Overridepublic void run() {synchronized (Demo07_DeadLock.lock1) {synchronized (Demo07_DeadLock.lock2) {System.out.println("取得第二把锁之后要做的事情");System.out.println("Thread2同时取得两把锁之后要做的事情");}}}}
17,线程组ThreadGroup 默认处于同一个组里面
使用线程组可以统一设置这个组内线程的一些东西。比如设置优先级,设置是否是守护线程。
package com.sikiedu.chapter1;public class MyRunnable implements Runnable {@Overridepublic void run() {for(int i=0;i<100;i++) {Thread t = Thread.currentThread();System.out.println(t.getName()+":"+i);}}}
package com.sikiedu.chapter1;public class Demo08_ThreadGroup {public static void main(String[] args) {MyRunnable r = new MyRunnable();ThreadGroup tg = new ThreadGroup("我们的线程组");Thread t1 = new Thread(tg,r);Thread t2 = new Thread(tg,r);System.out.println(tg.getName());//我们的线程组//批量设置线程属性t1.setDaemon(true);t1.setPriority(9);t1.interrupt();t1.start();t2.start();}static void DefalutThreadGroup(){// 不设置线程组的话默认处于同一个组里面MyRunnable r = new MyRunnable();Thread t1 = new Thread(r);Thread t2 = new Thread(r);ThreadGroup tg = t1.getThreadGroup();System.out.println(tg.getName());//mainSystem.out.println(t2.getThreadGroup().getName());//maint1.start();t2.start();}}
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();
package com.sikiedu.chapter1;import java.util.Timer;import java.util.TimerTask;public class Demo09_Timer {public static void main(String[] args) {Timer t = new Timer();// t.schedule(new MyTimerTask(), 2000);//等待2s,执行任务// t.schedule(new MyTimerTask(), 2000, 3000);//等待2s,然后每隔3s执行一次// t.cancel();//终止任务}}class MyTimerTask extends TimerTask{@Overridepublic void run() {System.out.println("定时器任务");}}
网络编程(两个计算机通信)
简述:跟网络相关的编程<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);
package com.sikiedu.chapter2;import java.net.InetAddress;import java.net.UnknownHostException;public class Demo01_InetAddress {public static void main(String[] args) throws Exception {InetAddress ip1 = InetAddress.getByName("DESKTOP-J8UV13P");//此电脑->右键->设备名称InetAddress ip2 = InetAddress.getByName("192.168.31.225");System.out.println(ip1);// DESKTOP-J8UV13P/192.168.31.225System.out.println(ip2);// /192.168.31.225}}
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发送和接收数据,并实现数据的循环接收和循环发送
package com.sikiedu.chapter2;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.util.Scanner;public class Demo02_UDP_Send {public static void main(String[] args) throws Exception {DatagramSocket ds = new DatagramSocket();Scanner s = new Scanner(System.in);while(true) {String str = s.nextLine();if(str.equals("end"))break;byte[] buf = str.getBytes();int length = buf.length;InetAddress address= InetAddress.getByName("192.168.31.225");//127.0.0.1 localhostint port = 7879;DatagramPacket dp = new DatagramPacket(buf, length, address, port);ds.send(dp);}ds.close();//释放资源}}
package com.sikiedu.chapter2;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class Demo03_UDP_Receive {public static void main(String[] args) throws Exception {DatagramSocket ds = new DatagramSocket(7879);//要指定 监听哪个端口号来接收数据while(true) {byte[] buf = new byte[1024];int length = buf.length;DatagramPacket dp = new DatagramPacket(buf, length);ds.receive(dp);//程序会在这里等待,等待数据的到来String str = new String( dp.getData(),0,dp.getLength() );InetAddress address = dp.getAddress();//发送端的地址// System.out.println(dp.getPort());//发送端的端口号,并不是接收端的7879端口,由系统自动分配,每次都不同,一般没啥作用System.out.println(address+"----"+str);}// ds.close();接收数据端需要不断监听,所以不需要关闭}}
7,实现双向聊天功能
package com.sikiedu.chapter2;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class ReceiveThread extends Thread {private int port;public ReceiveThread(int port) {this.port=port;}@Overridepublic void run() {DatagramSocket ds=null;try {ds = new DatagramSocket(port);while(true) {byte[] buf = new byte[1024];int length = buf.length;DatagramPacket dp = new DatagramPacket(buf, length);ds.receive(dp);//程序会在这里等待,等待数据的到来String str = new String( dp.getData(),0,dp.getLength() );InetAddress address = dp.getAddress();// System.out.println(dp.getPort());System.out.println(address+":"+str);}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(ds!=null)ds.close();}}}
package com.sikiedu.chapter2;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.util.Scanner;public class SendThread extends Thread {private int port;public SendThread(int port) {this.port = port;}@Overridepublic void run() {DatagramSocket ds=null;try {ds = new DatagramSocket();Scanner s = new Scanner(System.in);while(true) {String str = s.nextLine();if(str.equals("end"))break;byte[] buf = str.getBytes();int length = buf.length;InetAddress address= InetAddress.getByName("192.168.31.225");//127.0.0.1 localhostDatagramPacket dp = new DatagramPacket(buf, length, address, port);ds.send(dp);}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(ds!=null)ds.close();}}}
package com.sikiedu.chapter2;public class Demo04_Chat_User01 {public static void main(String[] args) {new SendThread(7878).start();new ReceiveThread(7879).start();}}
package com.sikiedu.chapter2;public class Demo05_Chat_User02 {public static void main(String[] args) {new ReceiveThread(7878).start();new SendThread(7879).start();}}
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
package com.sikiedu.chapter2;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.net.UnknownHostException;import java.util.Scanner;//使用tcp 发送端:client客户端 接收端server服务器端public class Demo06_TCP_Send {public static void main(String[] args) throws Exception {//Socket//TCP需要建立连接Socket s = new Socket("192.168.31.225", 7878);//建立跟指定ip port建立连接InputStream input = s.getInputStream();OutputStream output = s.getOutputStream();//得到输出流。使用流适合发送大容量的数据,数据大小不受限制,可以持续写数据,并保证稳定性//客户端接收数据。//如果不使用多线程的话。执行到while(true)时就永远不会执行下面的发送数据代码了,所以需要使用多线程并行new Thread() {public void run() {int length = -1;byte[] buf = new byte[1024];try {while(true) {if(s.isClosed())break;length = input.read(buf);if(length==-1)break;System.out.println(new String(buf,0,length));}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}.start();//客户端发送数据Scanner scanner = new Scanner(System.in);while(true) {String str = scanner.nextLine();if(str.equals("end"))break;//输出流写入数据output.write(str.getBytes());}s.close();}}
package com.sikiedu.chapter2;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.Scanner;public class Demo07_TCP_Receive {public static void main(String[] args) throws Exception {ServerSocket ss = new ServerSocket(7878);Socket client = ss.accept();//接收,建立跟发送端的连接。会等待客户端的连接System.out.println("接收到一个客户端的链接");//new Socket("192.168.0.107", 7878);发送端执行这行的时候,会发送跟接收端建立连接的请求InputStream input = client.getInputStream();//接收使用输入流OutputStream output = client.getOutputStream();//发送使用输出流//服务器端发送数据。//如果不使用多线程的话。执行到while(true)时就永远不会执行下面的发送数据代码了,所以需要使用多线程并行Thread t = new Thread() {public void run() {Scanner scanner = new Scanner(System.in);while(true) {if(client.isClosed())break;String str = scanner.nextLine();if(str.equals("end"))break;try {output.write(str.getBytes());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}};t.setDaemon(true);//设置成守护线程。防止客户端关闭连接时,服务器端卡在scanner.nextLine();//然而并无卵用,TODOt.start();//服务器端接收数据byte[] buf = new byte[1024];// while(true) {//接收数据不能使用死循环,因为客户端关闭连接时接收到的数据length是-1,需用下面这种方法// int length = input.read(buf);// System.out.println(length);// System.out.println(new String(buf,0,length));// }int length = -1;//-1代表流结束,客户端关闭连接close()时会返回-1while( (length = input.read(buf))!=-1 ) {//read()会等待客户端发送数据System.out.println(new String(buf,0,length));}client.close();ss.close();}}
10,聊天室功能
一个服务器端接收数据并输出,多个客户端可以持续的发送数据
一个服务器端接收数据并转发到所有客户端。
package com.sikiedu.chapter2;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class Demo08_TCP_Server {public static void main(String[] args) {new ConnectionThread().start();}}class ConnectionThread extends Thread {@Overridepublic void run() {ServerSocket ss = null;try {ss = new ServerSocket(7878);while(true) {Socket s = ss.accept();//监听客户端的连接请求System.out.println("接收到一个客户端连接:"+s.getInetAddress());new ClientThread(s).start();//必须开启线程,不然会阻塞下一个客户端请求}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {if(ss!=null)ss.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}class ClientThread extends Thread{private Socket s;public ClientThread(Socket s) {this.s= s;}@Overridepublic void run() {try {InputStream input= s.getInputStream();byte[] buf = new byte[1024];int length = -1;while( (length = input.read(buf))!=-1 ) {System.out.println(new String(buf,0,length));}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(s!=null)try {s.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
package com.sikiedu.chapter2;import java.io.IOException;import java.net.Socket;import java.net.UnknownHostException;import java.util.Scanner;public class Demo09_TCP_Client {public static void main(String[] args) throws Exception {Socket s = new Socket("192.168.31.225", 7878);Scanner scanner = new Scanner(System.in);s.getOutputStream().write(scanner.nextLine().getBytes());s.close();}}
枚举类型
作用:见名知意,增加代码可读性。<br /> <br /> <br /> 注意枚举类型的作用是见名知意,它底层还是使用int类型来存储和判断的。<br /> 枚举类型里面的每个值都有一个对应的int值。<br />
package com.sikiedu.chapter3;public class Demo01_CustomEnum {public static void main(String[] args) {RoleTypeEnum rt1 = RoleTypeEnum.TEACHER;RoleTypeEnum rt2 = RoleTypeEnum.STUDENT;if(rt1==RoleTypeEnum.PRINCIPAL) {//因为本质是int,所以可以直接==比较}SeasonEnum s1 = SeasonEnum.FALL;SeasonEnum s2= SeasonEnum.WINTER;System.out.println(s1);//FALL。直接输出SeasonEnum对象输出的是枚举所对应的字符串System.out.println(s1.ordinal());//2。获取枚举值对应的intfor(SeasonEnum se : SeasonEnum.values()) {//遍历枚举类型的所有值System.out.println(se);//SPRINT SUMMER FALL WINTER}}}public enum RoleTypeEnum {TEACHER,STUDENT,PRINCIPAL,HEADTEACHER,LOGISTICS}public enum SeasonEnum {SPRINT,SUMMER,FALL,WINTER}
类的加载机制和反射
类的使用分为三个步骤:
类的加载->类的连接->类的初始化
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(“类路径”)//必须传递完整路径(加上包名)
package com.sikiedu.chapter4;public class Demo01_GetClass {public static void main(String[] args) throws Exception {//获取Class对象//1,通过对象User user1 = new User(100,"siki","123");User user2 = new User(200,"edu","456");Class c1 = user1.getClass();System.out.println(c1);//class com.sikiedu.chapter4.UserClass c2 = user2.getClass();System.out.println(c2);//class com.sikiedu.chapter4.UserSystem.out.println(c1==c2);//true 每个类只有对应的一个Class对象Demo01_GetClass o = new Demo01_GetClass();Class c3 = o.getClass();System.out.println(c3==c2);//false c2是User的Class对象,c3是Demo01_GetClass的Class对象//2,通过类(很少用)Class c4 = User.class;System.out.println(c4==c1);//true//3,Class.forNameClass c5 = Class.forName("java.lang.Math");System.out.println(c5==c1);//falseClass c6 = Class.forName("com.sikiedu.chapter4.User");System.out.println(c6==c1);//true}}
5,从Class对象中获取信息
测试类User:
package com.sikiedu.chapter4;public class User {private int id;private String username;private String password;public int age;private User(int id) {this.id = id;}public User() {}public User(int id,String username,String password) {this.id = id;this.username = username;this.password = password;}public User(int id ,String username,String password,int age) {this.id = id;this.username = username;this.password = password;this.age = age;}public void show() {System.out.println(id+":"+username+":"+password);}public void study() {System.out.println("学习");}public void study(String courseName) {System.out.println(username+"正在学习"+courseName);}private void shower(String name) {System.out.println(username+"正在使用"+name+"洗澡");}}
5.1,得到构造方法:
getConstructors();//获取所有public构造方法信息
getConstructor(Class<?> … parameterTypes );//根据参数类型,获取指定参数类型的public构造方法
getDeclaredConstructor(Class<?> … parameterTypes );//忽略访问权限,获取构造方法
getDeclaredConstructors();//忽略访问权限,获取构造方法
package com.sikiedu.chapter4;import java.lang.reflect.Constructor;public class Demo02_GetConstructorByClass {public static Class c;public static void main(String[] args) throws Exception {c = Class.forName("com.sikiedu.chapter4.User");// getConstructors();// getConstructor();// getConstructorByPara();// getAllConstructors();// getSpecifiedConstructorByPara();}// 得到所有的public构造方法static void getConstructors() {Constructor[] cs = c.getConstructors();//for (Constructor con : cs) {System.out.println(con);}/** 输出: public com.sikiedu.chapter4.User() public* com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String) public* com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String,int)*/}// 得到指定的public构造方法,并调用构造方法// 1,得到默认的构造方法并调用static void getConstructor() throws Exception {Constructor con = c.getConstructor();System.out.println(con);// public com.sikiedu.chapter4.User()Object o1 = con.newInstance();User user = (User) o1;user.show();// 0:null:null}// 2,得到带参数的构造方法并调用static void getConstructorByPara() throws Exception {Constructor con = c.getConstructor(int.class, String.class, String.class);System.out.println(con);// public com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String)Object o1 = con.newInstance(100, "siki", "123");User user = (User) o1;user.show();// 100:siki:123}// 忽略访问权限,得到所有的构造函数(public + private)static void getAllConstructors() {Constructor[] cs = c.getDeclaredConstructors();for (Constructor con : cs) {System.out.println(con);}/** 输出 public* com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String,int) public* com.sikiedu.chapter4.User(int,java.lang.String,java.lang.String) public* com.sikiedu.chapter4.User() private com.sikiedu.chapter4.User(int)*/}// 忽略访问权限,得到带参数的构造方法并调用(public + private)static void getSpecifiedConstructorByPara() throws Exception {Constructor con = c.getDeclaredConstructor(int.class);System.out.println(con);// private com.sikiedu.chapter4.User(int)con.setAccessible(true);// 这里必须加这句才能调用private的构造方法Object o1 = con.newInstance(100);User user = (User) o1;user.show(); // 100:null:null}}
5.2,得到成员变量
getFields
getDeclaredFields
getField
getDeclaredField
package com.sikiedu.chapter4;import java.lang.reflect.Field;//构造方法 成员变量 成员方法public class Demo03_GetFieldByClass {public static void main(String[] args) throws Exception {User u = new User(100,"siki","123",40);Class c = Class.forName("com.sikiedu.chapter4.User");// //得到所有的public字段(成员变量)// Field[] fields = c.getFields();// for( Field f : fields ) {// System.out.println(f);//public int com.sikiedu.chapter4.User.age// }// //得到指定的public字段(成员变量)// Field ageField = c.getField("age");// System.out.println(ageField);//public int com.sikiedu.chapter4.User.age// int age = ageField.getInt(u);// System.out.println(age);//40 通过反射的方式访问成员变量 正常情况:u.age 反射情况:age.u// //忽略访问权限,得到所有的字段(public + private)// Field[] fields = c.getDeclaredFields();// for( Field f : fields ) {// System.out.println(f);// }// /*// * private int com.sikiedu.chapter4.User.id// private java.lang.String com.sikiedu.chapter4.User.username// private java.lang.String com.sikiedu.chapter4.User.password// public int com.sikiedu.chapter4.User.age//// * */// //忽略访问权限,得到制定的字段(public + private)// Field usernameField = c.getDeclaredField("username");// usernameField.setAccessible(true);// System.out.println(usernameField.get(u));//siki}}
5.3,得到成员方法
getMethod
getMethods
getDeclaredMethod
getDeclaredMethods
package com.sikiedu.chapter4;import java.lang.reflect.Method;public class Demo04_GetMethodByClass {public static void main(String[] args) throws Exception {User u = new User(100,"siki","123",40);Class c = Class.forName("com.sikiedu.chapter4.User");// //得到所有public方法,包括父类的// Method[] methods = c.getMethods();// for(Method m :methods) {// System.out.println(m);// }// /*// * public void com.sikiedu.chapter4.User.show()// public void com.sikiedu.chapter4.User.study(java.lang.String)// public void com.sikiedu.chapter4.User.study()// public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException// public final void java.lang.Object.wait() throws java.lang.InterruptedException// public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException// public boolean java.lang.Object.equals(java.lang.Object)// public java.lang.String java.lang.Object.toString()// public native int java.lang.Object.hashCode()// public final native java.lang.Class java.lang.Object.getClass()// public final native void java.lang.Object.notify()// public final native void java.lang.Object.notifyAll()// * *///// //得到指定的public方法// //u.show() show.u// Method m= c.getMethod("show");// Object o =m.invoke(u);//100:siki:123// System.out.println(o);//null// Method studyMethod = c.getMethod("study", String.class);// studyMethod.invoke(u, "生物");//siki正在学习生物// //忽略访问权限,得到所有的自身(不包括父类)方法(public + private)// Method[] methods = c.getDeclaredMethods();//只得到自身的所有方法(包括private),不包括父类里面的方法// for(Method m :methods) {// System.out.println(m);// }// /*// * private void com.sikiedu.chapter4.User.shower(java.lang.String)// public void com.sikiedu.chapter4.User.study()// public void com.sikiedu.chapter4.User.study(java.lang.String)// public void com.sikiedu.chapter4.User.show()// * */// //忽略访问权限,得到指定的自身(不包括父类)方法(public + private)// Method showerM = c.getDeclaredMethod("shower", String.class);// showerM.setAccessible(true);// showerM.invoke(u, "海飞丝");//siki正在使用海飞丝洗澡}}
Constructor成员:
newInstance
setAccessible
