一、序列化+反序列化
1.1序列化+反序列化概念
/** * 序列化:就是使用ObjectOutputStream将一个Java对象的信息写入流中 * * 对象--->数据流(流数据) * 构造方法: public ObjectOutputStream(OutputStream out) * 成员方法:public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据 * * * 反系列化:就是使用ObjectInputStream 将数据流中的内容--->还原成--->对象 * 构造方法: * public ObjectInputStream(InputStream in) * 成员方法: * public final Object readObject():从反序列流中将数据流的内容----解析成对象 * */
1.2 代码实现
public class ObjectStream { public static void main(String[] args) throws IOException, ClassNotFoundException { // write() ; read() ; } //读:将流数据---还原成--->对象 public static void read() throws IOException, ClassNotFoundException { //创建反序列化流对象ObjectInputStream ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ; // public final Object readObject():从反序列流中将数据流的内容----解析成对象 Object object = ois.readObject(); //强转学生类型 Student s = (Student) object; System.out.println(s.getName()+"---"+s.getAge()); } //写数据 public static void write() throws IOException { //创建序列化流对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ; //public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据 //创建一个学生对象 Student s = new Student("高圆圆",44) ; oos.writeObject(s) ; //java.io.NotSerializableException: com.qf.io_01.Student 当前这个类未实现序列化异常 } }
package com.qf.io_01; import java.io.Serializable; /** * 学生类 *当没有去实现Serializable接口时,会出现.NotSerializableException异常! * 只有支持java.io.Serializable接口的对象才能写入流中 * 当前这个类能够实现java.io.Serializable接口---->这个类对象就可以被序列化(对象---流数据) * java提供一些接口:如果字段,连成员方法都没有,都称为"标记接口" * * * 序列化的时候--->当前类的所有的成员(成员变量/成员方法/构造方法等等)---->内存中--->"类的签名信息" 序列化的ID值 * 如果反序列化的时候,修改了类的成员信息,会导致和之前序列化的信息不一致:就会出现 * java.io.InvalidClassException:序列化中的序列化版本UID值和反序列化的UID值不相同(类的签名信息不一致) * */ public class Student implements Serializable { //生成固定的序列化的版本Id private static final long serialVersionUID = -5794781839275410852L; String name ; transient int age ; //transient 让这个字段(属性) 不参与序列化和反序列化 public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
二、反射
2.1 反射的概念
(Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class) * 创建正在运行类的对象(构造器-Constructor) * 使用正在运行的这个类的对象去给 成员变量赋值(Field) * 调用正在运行的这个类中成员方法(Method) * 反射应用----Java设计模式 * 正射 ----就是我们自己书写的代码的时候, 类名 对象名 = new 类名() ;
2.2 面试题 获取一个类的字节码文件的方式?
1)调用Object类的getClass方法 2)任意Java类型的class属性 3)第三种方式:(推荐) Java提供类Class:表示正在运行的Java应用程序中的类和接口 有一个静态方法: 参数:类的完全限定名称。 (包名.类名) String类型:经常用在配置文件中xx.properties public static Class<?> forName(String className):
2.3 反射的基本使用(获取构造方法创建对象有参传值)
/** * (Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class) * 然后--->创建当前类对象 * * 正常在写代码: * Person p = new Person() ; //创建Person类的对象 */ public class ReflectDemo2 { public static void main(String[] args) throws Exception{ System.out.println("正常的创建对象的格式:"); Person p = new Person() ; System.out.println(p); System.out.println("---------------------反射的方式:动态获取类的构造器并创建实例---------------------------------"); //1)获取正在运行的类的字节码文件对象 Class c = Class.forName("com.qf.reflect_02.Person"); //2)public Constructor<?>[] getConstructors()throws SecurityException //获取正在运行的类中的所有公共的构造方法所在的Constructor类对象 //public Constructor<?>[] getDeclaredConstructors(): //获取正在运行的类中所有的构造方法所在的Constructor类对象 // Constructor[] cons = c.getConstructors(); /* Constructor[] cons = c.getDeclaredConstructors(); for(Constructor con:cons){ System.out.println(con); }*/ //2)获取指定的构造器的Constructor对象,创建当前类实例 //public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取指定的公共的构造方法的Constructor对象,参数:指定参数类型的Class类对象,如果没有参数,可以不写 //举例:String类型的参数---java.lang.String //int---->java.lang.Integer Constructor con = c.getConstructor();//无参构造器 //System.out.println(con);//public com.qf.reflect_02.Person() //Constructor提供一个方法 //参数:通过构造器里面给当前正在运行的类的成员变量赋值:实际参数 //返回T---Type:代表任意Java对象 //public T newInstance(Object... initargs):创建当前类实例 Object obj = con.newInstance() ; // 多态 类似于 --->Object obj = new Person() ; System.out.println(obj); //Person p2 = (Person) obj; // System.out.println(p2) ; System.out.println("----------------通过有参构造方法当前类实例---------------------------------"); //Person person = new Person("高圆圆",44,"西安市") ;// 正常:私有不能直接创建 //通过当前类的字节码文件对象获取指定的构造方法 //参数是:当前参数类型的字节码文件 String--->String.class //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor con2 = c.getDeclaredConstructor(String.class, int.class, String.class);//有参构造器 //System.out.println(con2); //构造器所在Constructor继承AccessibleObject 提供 // public void setAccessible(boolean flag) :参数为true:取消Java语言访问检查 con2.setAccessible(true); //public T newInstance(Object... initargs):创建当前类实例 Object obj2 = con2.newInstance("高圆圆", 44, "西安市南窑国际"); System.out.println(obj2); } //public static void get(int...a){ //a代表多个参数:可变参数(当不明确具体有多个参数的时候,可以使用...) //} }
2.4 访问成员变量(代码实现)
package com.qf.reflect_03; import com.qf.reflect_02.Person; import java.lang.reflect.Method; /** * 动态获取指定类的字节码文件对象,创建当前类实例,调用里面的指定的成员方法! * * 之前写: * Person p = new Person() ; * p.show("javaee") ; */ public class ReflectDemo4 { public static void main(String[] args) throws Exception{ System.out.println("-------------之前的写法-----------") ; Person p = new Person() ; p.show("helloworld") ; System.out.println("------------------反射的方式-------------------------"); //1)获取指定类的字节码文件对象 Class c = Class.forName("com.qf.reflect_02.Person") ; //2)无参构造方法公共的,Person类中,字节创建当前类实例 Object obj = c.newInstance() ; //public Methods[] getMethods():获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法) //public Methods[] getDeclaredMethods():获取类所有的的指定的成员方法的类对象Method,不包括继承过来的 //3)获取指定的公共成员方法 //public Method getMethod(String name,Class<?>... parameterTypes) //参数1:方法的名称 //参数2:当前方法中参数类型的Class Method m = c.getMethod("show", String.class);//参数2:java.lang.String //System.out.println(m); //4)调用方法 //Method--->public Object invoke(Object obj,Object... args) //参数1:这个类的实例 //参数2:方法中传入的实际参数 m.invoke(obj, "helloworld");//本身这个方法没有返回值,可以不写;有返回值,使用Object类型接收 System.out.println("-------------------------------------------------") ; //调用method方法 //获取指定的成员方法所在的类对象Method //public Method getDeclaredMethod(String name,Class<?>... parameterTypes) Method m2 = c.getDeclaredMethod("method", int.class) ; //System.out.println(m2) ; //取消Java语言检查 m2.setAccessible(true) ; //调用方法 Object returnObj = m2.invoke(obj, 100); System.out.println(returnObj); //其他方法调用 } }
2.5 访问成员方法
package com.qf.reflect_03; import com.qf.reflect_02.Person; import java.lang.reflect.Method; /** * 动态获取指定类的字节码文件对象,创建当前类实例,调用里面的指定的成员方法! * * 之前写: * Person p = new Person() ; * p.show("javaee") ; */ public class ReflectDemo4 { public static void main(String[] args) throws Exception{ System.out.println("-------------之前的写法-----------") ; Person p = new Person() ; p.show("helloworld") ; System.out.println("------------------反射的方式-------------------------"); //1)获取指定类的字节码文件对象 Class c = Class.forName("com.qf.reflect_02.Person") ; //2)无参构造方法公共的,Person类中,字节创建当前类实例 Object obj = c.newInstance() ; //public Methods[] getMethods():获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法) //public Methods[] getDeclaredMethods():获取类所有的的指定的成员方法的类对象Method,不包括继承过来的 //3)获取指定的公共成员方法 //public Method getMethod(String name,Class<?>... parameterTypes) //参数1:方法的名称 //参数2:当前方法中参数类型的Class Method m = c.getMethod("show", String.class);//参数2:java.lang.String //System.out.println(m); //4)调用方法 //Method--->public Object invoke(Object obj,Object... args) //参数1:这个类的实例 //参数2:方法中传入的实际参数 m.invoke(obj, "helloworld");//本身这个方法没有返回值,可以不写;有返回值,使用Object类型接收 System.out.println("-------------------------------------------------") ; //调用method方法 //获取指定的成员方法所在的类对象Method //public Method getDeclaredMethod(String name,Class<?>... parameterTypes) Method m2 = c.getDeclaredMethod("method", int.class) ; //System.out.println(m2) ; //取消Java语言检查 m2.setAccessible(true) ; //调用方法 Object returnObj = m2.invoke(obj, 100); System.out.println(returnObj); //其他方法调用 } }
2.6 反射的应用
package com.qf.reflect_test_01; import com.sun.corba.se.spi.orbutil.threadpool.Work; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /** * 测试类 * * 开发原则:所有开发原则必须"低耦合,高内聚" * 开闭原则: * 对现有代码的修改进行关闭,对扩展开放! */ public class ReflectTest { public static void main(String[] args) throws Exception { //使用学生的love方法 /*Student s = new Student() ; s.love() ;*/ //切换为工人类的love方法 Worker worker = new Worker() ; worker.love(); System.out.println("--------------------------------------------") ; //使用反射:--src下面提供xx.properties配置文件 //创建一个属性集合列表---空的 Properties prop = new Properties() ; //读取src下面的配置文件 InputStream inputStream = ReflectTest.class.getClassLoader(). getResourceAsStream("my.properties"); //将配置文件中的内容 以"字节输入流"的方式加载到属性集合列表中 prop.load(inputStream); // System.out.println(prop); //通过属性集合列表特有功能:getProperty(String key)---获取String value String className = prop.getProperty("className"); //System.out.println(className) ; String methodName = prop.getProperty("methodName"); //System.out.println(methodName); //通过反射获取当前类的字节码文件对象 Class c = Class.forName(className) ;//class com.qf.reflect_test_01.Student //直接创建当前类实例 Object obj = c.newInstance();//等价于字节码文件对象获取无参构造方法的Constructor类对象--->Object newIntance() //System.out.println(obj); //获取当前类的成员方法的method类对象 Method method = c.getDeclaredMethod(methodName); //调用方法 method.invoke(obj) ; } } package com.qf.reflect_test_01; /** * 学生类 */ public class Student { public void love(){ System.out.println("爱学习,爱Java!"); } } package com.qf.reflect_test_01; /** * 工人类 */ public class Worker { public void love(){ System.out.println("爱生活,爱高圆圆!"); } } my.properties className=com.qf.reflect_test_01.Worker methodName=love
2.7 反射中的常见面试题
1)有一个集合ArrayList<String>,存储了一些字符串数据了,如何给里面添加Integer类型的数据? add(E e)--->只能通过反射获取到add方法---所在的Method类对象 2)利用反射解决代码重复修改---->将一些内容存储到配置文件中 public class ReflectTest { public static void main(String[] args) throws Exception { ArrayList <String> list = new ArrayList<>(); //正常只能添加String类型的数据 list.add("hello"); list.add("100"); //获取ArrayList的字节码文件名称 System.out.println(ArrayList.class); //获取ArrayList的字节码文件的对象 Class<?> c = Class.forName("java.util.ArrayList"); //获取指定成员方法 Method add = c.getDeclaredMethod("add",Object.class); //调用方法 add.invoke(list,108); System.out.println(list); } }
2.8 反射的jdk动态代理(理解并且掌握)
* 代理设计模式---属于"结构型设计模式" * 代理核心思想:代理角色帮助真实角色对他的业务功能进行增强! * 静态代理 * 特点:代理角色和真实角色必须实现同一个接口! * 弊端:业务功能和 增强的功能没有完全分离! * 动态代理 * jdk动态代理--->jdk提供的 * java.lang.reflect.Proxy:动态代理类 * 静态方法: * public static Object newProxyInstance( * ClassLoader loader, //获取接口实例的类加载器 * Class<?>[] interfaces,//实现的接口列表的字节码文件(代理类要的实现接口列表的Class) * InvocationHandler h)//有代理实例调用 接口方法的处理程序:接口类型 throws IllegalArgumentException * InvocationHandler:接口 * public Object invoke(Object proxy, Method method, Object[] args) * 参数1:调用该方法的代理实例 * 参数2:调用该代理实例的接口方法的Method类对象 * 参数3:调用接口方法中所有的参数,组成数组,没有参数,可不写 * * cglib动态代理----不需要接口,基于类就可以实现动态代理 * 必须提供第三方jar包 (cglib-3.3.0.jar) * */
package com.qf_reflect_02; /** * 针对用户的数据访问接口 * 这个接口有针对用户的添加,删除,修改,查询的功能 */ public interface UserDao { /** * 添加用户操作 */ void add() ; /** * 修改用户 */ void update() ; /** * 删除用户 */ void delete() ; /** * 查询所有用户 */ void findAll() ; } package com.qf_reflect_02; /** * 针对用户的数据接口实现 */ public class UserDaoImpl implements UserDao{ /** * 添加用户操作 */ @Override public void add() { System.out.println("添加用户操作") ; } /** * 修改用户 */ @Override public void update() { System.out.println("修改用户") ; } /** * 删除用户 */ @Override public void delete() { System.out.println("删除用户") ; } /** * 查询所用 */ @Override public void findAll() { System.out.println("查询所有用户") ; } } package com.qf_reflect_02; /** * 优化之后的实现类:对add/delete/udate/findAll这些功能进行增强! */ public class UserDaoImpl2 implements UserDao{ private UserDao ud ; public UserDaoImpl2(UserDao ud){ this.ud = ud ; } @Override public void add() { System.out.println("权限校验") ; //---->第三方提供的: 系统监控代码 shiro框架:权限框架/SpringSecurity权限框架 //自己的业务功能 ud.add() ; System.out.println("产生日志记录"); } @Override public void update() { System.out.println("权限校验" ) ; //自己的业务功能 ud.update(); ; System.out.println("产生日志记录"); } @Override public void delete() { System.out.println("权限校验" ) ; //自己的业务功能 ud.delete(); ; System.out.println("产生日志记录"); } @Override public void findAll() { System.out.println("权限校验" ) ; //自己的业务功能 ud.findAll(); ; System.out.println("产生日志记录"); } } package com.jdkproxy_03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 自定义一个类实现InvocationHandler接口 * 实现invoke方法---->就是调用接口方法 * * 代理实例要调用接口方法的处理程序 */ public class MyInvocationHandler implements InvocationHandler { //声明Object变量 private Object target ; public MyInvocationHandler(Object target){// 传入真实角色 this.target = target ; } /** * * * * * @param proxy 调用该方法的代理实例 * @param method 调用该代理实例的接口方法的Method类对象 * @param args 调用接口方法中所有的参数,组成数组,没有参数,可不写 * @return 从代理实例上的方法调用返回的值 * @throws Throwable 可能调用方法失败! */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("操作业务功能之前,先权限校验!" ); //反射的写法调用接口方法--add()/delete()/udate()/findAll() Object obj = method.invoke(target, args); System.out.println("操作业务功能之后,产生日志记录!"); return obj; //方法的返回值 } } package com.qf_reflect_02; import com.jdkproxy_03.MyInvocationHandler; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 测试类 * 提供好了一个接口,---对用户的添加删除修改查询的功能 * 在具体的实现类中进行实现类; * 需求: * 想对添加/删除/修改/查询的功能之前,完成一些事情 "权限校验"功能,操作这些业务方法之后,产生"日志记录"这些事情! * * */ public class UserTest { public static void main(String[] args) { //创建UserDao接口对象---子实现类创建 UserDao ud = new UserDaoImpl() ; //ud:真实角色 ud.add() ; ud.update() ; ud.delete() ; ud.findAll(); System.out.println("------------------优化后:对业务功能增强(静态代理)-------------------") ; UserDaoImpl2 ud2 = new UserDaoImpl2(ud) ; //代理角色 ud2.add() ; ud2.delete(); ud2.update(); ud2.findAll(); System.out.println("---------------------JDK动态代理实现--------------------------------------------") ; //接口多态 InvocationHandler handler = new MyInvocationHandler(ud) ; UserDao ud3 = (UserDao) Proxy.newProxyInstance( ud.getClass().getClassLoader(),//代理实例要实现的接口的字节码文件---类加载 //代理实例要实现接口列表的Class--->Class类中--public 类<?>[] getInterfaces() ud.getClass().getInterfaces(), //代理实例调用接口方法的处理程序 handler); ud3.add() ; ud3.delete() ; ud3.update() ; ud3.findAll() ; } }
三、网络聊天室(第一版)
publicclassClientTcp01{ publicstaticvoidmain(String[]args){ Sockets=null; try{ //创建Socket对象 s=newSocket("127.0.0.1",10089); //获取客户端通道的输出和输入流 InputStreamin=s.getInputStream(); OutputStreamout=s.getOutputStream(); //创建键盘录入对象 Scannersc=newScanner(System.in); //客户端不断的发送消息 while(true){ System.out.println("请输入消息:"); Stringmsg=sc.nextLine(); //使用输出流发送过去 out.write(msg.getBytes()); //客户端读取服务器发送的消息 byte[]bys=newbyte[1024]; intlen=in.read(bys); StringmsgStr=newString(bys,0,len); System.out.println("客户端收到服务器发送的消息:"+msgStr); } }catch(IOExceptione){ e.printStackTrace(); }finally{ if(s!=null){ try{ s.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } } publicclassServerTcp{ publicstaticvoidmain(String[]args){ ServerSocketss=null; try{ //创建服务器端Socket对象 ss=newServerSocket(10089); //监听客户端 Sockets=ss.accept(); //获取服务器端通道的输出和输入流 InputStreamin=s.getInputStream(); OutputStreamout=s.getOutputStream(); //创建键盘录入对象 Scannersc=newScanner(System.in); //服务器端不断的读取消息和回复消息 while(true){ //读消息 byte[]bys=newbyte[1024]; intlen=in.read(bys); Stringmsg=newString(bys,0,len); //回复消息 System.out.println("服务器端收到消息"+msg+"并回复消息:"); StringmsgStr=sc.nextLine(); out.write(msgStr.getBytes()); } }catch(IOExceptione){ e.printStackTrace(); }finally{ if(ss!=null){ try{ ss.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } }
第二版(网络聊天优化使用集合)线程加集合
package com.qf.chatroom.client; import com.qf.chatroom.thread.ClientReadThread; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; /** * 网络聊天: * TCP方式---保证安全(可靠连接,建立连接通道) * 客户端不断的发送消息,不断的去读取服务器端反馈的消息! */ public class Client { public static void main(String[] args) { try { //创建客户端的Socket对象 Socket socket = new Socket("10.35.165.17",8888) ; //获取客户端所在的通道内的字节输入和字节输出流 OutputStream out = socket.getOutputStream() ; InputStream in = socket.getInputStream(); //创建键盘录入对象 Scanner sc = new Scanner(System.in) ; //客户端开启读取服务器回复的消息的子线程 ClientReadThread crt = new ClientReadThread(socket) ; crt.start() ; //不断的去发送消息 while (true){ System.out.println("请发送消息:" ); String message = sc.nextLine() ; //约定客户端发送消息的格式: "接收者:消息内容:发送者" (:英文符号) out.write(message.getBytes()) ; //读取服务器反馈的消息 /*byte[] bytes = new byte[1024] ; int len = in.read(bytes); String fkMessage = new String(bytes,0,len) ; //展示服务器反馈的消息 System.out.println(fkMessage);*/ } /** * 在用户线程main中,客户端和服务器端都一样,既去写消息,又要读消息, * read方法本身就是阻塞式方法,如果服务器端监听多个客户端,多个客户端同时去给服务器端消息,可能消息阻塞现象! * 不安全!--------->开启多线程的方式 * 一般子线程中:"读消息",不会录入的消息 (main用户线程中录入) * 客户端和服务器端---分别开启读消息的子线程-----使用 继承Thread类实现 */ } catch (IOException e) { e.printStackTrace(); } } } //客户端线程 package com.qf.chatroom.thread; import java.io.IOException; import java.io.InputStream; import java.net.Socket; /** * 客户端读取服务器发送过来的消息的子线程 */ public class ClientReadThread extends Thread { private Socket s ; public ClientReadThread(Socket s) { this.s = s ; } @Override public void run() { //不断的去读 try { while(true){ //获取客户端通道内的字节输入流 InputStream in = s.getInputStream(); byte[] bytes = new byte[1024] ; int len = in.read(bytes); String fkMessage = new String(bytes,0,len) ; //展示服务器反馈的消息 System.out.println(fkMessage); } } catch (IOException e) { e.printStackTrace(); }finally { if(s!=null){ try { s.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 服务器端: package com.qf.chatroom.server; import com.qf.chatroom.thread.ServerReadThread; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Scanner; /** * 服务器端: * 不断读取客户端写过来的消息,给客户端不断回复数据 * * 真实场景: * 服务器端不回复消息,作用转发消息,一般客户端和客户端聊天! * 张三客户端---->服务器端----->消息转发----->李四客户端 */ public class Server { public static void main(String[] args) { //创建服务器端的Socket try { ServerSocket ss = new ServerSocket(8888) ; //创建List集合--默认ArrayList,存储每一个客户端! ArrayList<Socket> list = new ArrayList<>() ; System.out.println("服务器正在等待连接..."); int i = 1 ; while(true){ //监听客户端连接 Socket socket = ss.accept() ; //不断的监听客户端连接 System.out.println("第"+(i++)+"个客户端已连接..."); //获取到的客户端通道内的字节输入和字节输出流 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream() ; //将每一个客户端所在Socket对象存储在List集合中 list.add(socket) ; System.out.println(list) ; //开启服务器端读取客户端消息的子线程 ServerReadThread srt = new ServerReadThread(socket,list) ; srt.start(); } //创建键盘录入对象 // Scanner sc = new Scanner(System.in) ; //不断去读取消息和回复消息 /*while(true){ //不断的回消息 System.out.println("请回复消息: ") ; String fkMessage = sc.nextLine() ; out.write(fkMessage.getBytes()); //服务器不关闭 }*/ } catch (IOException e) { e.printStackTrace(); } } } 服务器线程端: package com.qf.chatroom.thread; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.ArrayList; /** * 服务器端读取客户端发送过来的消息的子线程 */ public class ServerReadThread extends Thread{ private Socket s ; private ArrayList<Socket> list ; public ServerReadThread(Socket s,ArrayList<Socket> list) { this.s = s ; this.list = list ; } @Override public void run() { try { while(true){ //获取到监听客户端所在的通道内的字节输入流 InputStream in = s.getInputStream(); //不断去读取客户端发送过来的消息 byte[] bytes = new byte[1024] ; int len = in.read(bytes); //展示客户端发送的消息以及ip String ip = s.getInetAddress().getHostAddress() ; String receiveMessage = new String(bytes,0,len) ; System.out.println("消息是:"+receiveMessage) ; //receiveMessage "接收者:消息内容:发送者"(客户端发送端 消息格式) //举例:两个客户端: 0 1 //"1:你好:0" //通过":"进行字符串拆分 String[] strs = receiveMessage.split(":") ; //strs[0] :接收者 String receiver = strs[0] ; //strs[1] :消息内容 String messageContent = strs[1] ; //strs[2]:发送者 String sender = strs[2] ; //获取接收者所在的通道内的Socket对象 ArrayList<Socket>通过的角标获取Socket对象 Socket receiverSocket = list.get(Integer.parseInt(receiver)) ; //获取接收者所在通道内的字节输出流 OutputStream out = receiverSocket.getOutputStream(); //服务器端组织给客户端的消息--->写给接收者 out.write((sender+"对你说"+messageContent).getBytes()) ; //服务器转发消息 //不展示数据 //System.out.println(ip+"---"+receiveMessage) ; } } catch (IOException e) { e.printStackTrace(); } //服务器端不关闭 } }