Socket、反射
Socket
- UDP
public class UDPDemo {
public static void main(String[] args) {
new Receive().start();
new Send().start();
}
}
class Receive extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(6666); // 创建Socket 相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);// 创建Packet 相当于创建集装箱
while (true) {
socket.receive(packet); // 接货 接收数据
byte[] arr = packet.getData(); // 获取数据
int len = packet.getLength(); // 获取有效字节个数
String ip = packet.getAddress().getHostAddress(); // 获取IP地址
int port = packet.getPort(); // 获取端口号
System.out.println(ip + ":" + port + ":" + new String(arr, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Send extends Thread {
public void run() {
try {
Scanner sc = new Scanner(System.in); // 创建键盘录入对象
DatagramSocket socket = new DatagramSocket(); // 创建Socket 相当于创建码头
while (true) {
String line = sc.nextLine(); // 获取键盘录入的字符串
if ("quit".equals(line)) {
break;
}
DatagramPacket packet = // 创建Packet 相当于创建集装箱
new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); // 发送 将数据发出去
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
TCP
1.客户端
1)创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器;
2)调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
3)输入流可以读取服务端输出流写出的数据
4)输出流可以写出数据到服务端的输入流
2.服务端
1)创建ServerSocket(需要指定端口号)
2)调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
3)调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
4)输入流可以读取客户端输出流写出的数据
5)输出流可以写出数据到客户端的输入流
public class TCPClientDemo {
public static void main(String[] args){
try {
Socket socket = new Socket("127.0.0.1", 9999); //创建Socket指定ip地址和端口号
InputStream is = socket.getInputStream(); //获取输入流
OutputStream os = socket.getOutputStream(); //获取输出流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
PrintStream ps = new PrintStream(os);
System.out.println(br.readLine());
ps.println("我想学Java");
System.out.println(br.readLine());
ps.println("爷不学了");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TCPServerDemo {
public static void main(String[] args){
ServerSocket server = null; //创建服务器
try {
server = new ServerSocket(9999);
while(true) {
final Socket socket = server.accept(); //接受客户端的请求
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("欢迎咨询心理医生");
System.out.println(br.readLine());
ps.println("没救了");
System.out.println(br.readLine());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
反射
类加载机制
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。类的加载
类加载是指将类的class文件(字节码文件)载入内存中,并为之创建一个java.lang.Class对象,称之为字节码对象。类的连接
1)验证:检测被加载的类是否有正确的内部结构;
2)准备:负责为类的Static变量分配内存,并设置默认值;
3)解析:把类的二进制数据中的符号引用替换为直接引用。类的初始化
1)如果该类还未被加载和连接,则程序先加载并连接该类;
2)如果该类的直接父类还未被加载,则先初始化其父类;
3)如果该类中有初始化语句,则系统一次执行这些初始化语句。-
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;获取类的字节码对象:
// 方式一:通过Class中的静态方法forName(String className),读取配置文件(源文件阶段),读取配置文件
try {
Class<?> c3 = Class.forName("java.util.Date");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 方式二:使用class属性(字节码阶段),当做静态方法的锁对象
Class<?> c1 = Date.class;
// 方式三:通过对象的getClass方法获取(创建对象阶段),判断两个对象是否是同一个字节码文件
Date date = new Date();
Class<?> c2 = date.getClass();
- Class.forName()读取配置文件举例
public class ReflectDemo {
public static void main(String[] args){
// Juicer juicer = new Juicer();
// juicer.run(new Apple());
// juicer.run(new Orange());
// 用反射配置
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("src/Reflect/Config")); // //创建输入流对象,关联配置文件
Class<?> clazz = Class.forName(bufferedReader.readLine()); // 读取配置文件一行内容,获取该类的字节码对象
Fruit fruit = (Fruit) clazz.newInstance(); // //通过字节码对象创建实例对象
Juicer juicer1 = new Juicer();
juicer1.run(fruit);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
interface Fruit {
public void squeeze();
}
class Apple implements Fruit{
@Override
public void squeeze() {
System.out.println("榨出一杯苹果汁");
}
}
class Orange implements Fruit{
@Override
public void squeeze() {
System.out.println("榨出一杯橘子汁");
}
}
class Juicer {
public void run(Fruit fruit){
fruit.squeeze();
}
}
// Config内容:
Reflect.Orange
// 直接修改配置文件即可,不需要修改代码
- 通过反射获取带参构造方法
Class 类的 newInstance() 方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class 类的 getConstructor(String.class,int.class) 方法获取一个指定的构造函数然后再调用Constructor类的newInstance("张三",20)方法创建对象。
Class<?> clazz1 = Exception.Person.class;
Constructor constructor = clazz1.getConstructor(String.class);
Exception.Person person = (Person) constructor.newInstance("xiaolan");
// 调用私有构造器
Constructor<User> constructor = null;
constructor = clz.getDeclaredConstructor(String.class, int.class);
// 设置当前构造器能够访问
constructor.setAccessible(true);
User user1 = constructor.newInstance("xiaobai", 22);
- 通过反射获取成员变量
Class.getField(String) 方法可以获取类中的指定字段(可见的), 如果是私有的可以用 getDeclaedField("name") 方法获取,通过 set(obj, "李四") 方法可以设置指定对象上该字段的值, 如果是私有的需要先调用 setAccessible(true) 设置访问权限,用获取的指定的字段调用 get(obj) 可以获取指定对象中该字段的值。
constructor = clazz1.getConstructor(String.class);
Person person = (Person) constructor.newInstance("xiaolan");
Field field = clazz1.getField("age"); // 获取age字段
Field field1 = clazz1.getDeclaredField("name"); // 获取私有字段
System.out.println(person.age);
field.set(person, 20); // 修改相应字段的值
System.out.println(person.age);
- 通过反射获取方法并使用
Method[] methods = clazz1.getMethods(); // /获取public方法
Method[] methods1 = clazz1.getDeclaredMethods(); // 获取所有方法,包括私有方法
Method method = clazz1.getMethod("sleep"); // 获取指定方法
method.invoke(person);
Method method1 = clazz1.getMethod("play", String.class); // 获取指定带参数的方法
method1.invoke(person, "吃鸡");
Method method2 = clazz1.getDeclaredMethod("doWork");
method2.setAccessible(true);
method2.invoke(person);
Method method3 = clazz1.getMethod("eat", String.class, int.class);
method.invoke(null, "apple", 2); // 调用静态方法
-
九大内置class实例
JVM中预先提供好的Class实例:
byte,short,int,long,float,double,boolean,char,void
Integer和int是不同的数据类型:
Integer.class != int.class;
Integer.TYPE == int.class;类的Class实例:
// 数组的class实例
ArrayList array = new ArrayList();
Class arrayClass1 = array.getClass();
Class arrayClass2 = ArrayList.class;
- 模版(Template)设计模式
模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现。
优点:
使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求;
缺点:
如果算法骨架有修改的话,则需要修改抽象类;