TCP传输图片框架
package com.cskaoyan.week5.day28;
import java.io.*;
import java.net.Socket;
public class TCPSender {
public static void main(String[] args) throws IOException, InterruptedException {
String[] files = {"a.jpg", "b.jpg","c.jpg"};
for (int i = 0; i < files.length; i++) {
new UploadSenderThread(files[i]).start();
}
}
}
class UploadSenderThread extends Thread {
public String fileName;
public UploadSenderThread (String fileName) {
this.fileName = fileName;
}
@Override
public void run() {
//1. 创建tcp的发送端(客户端)Socket对象,并且和指定的 接收端ip和端口的 socket建立连接
Socket socket = null;
byte[] buffer = new byte[1024];
BufferedInputStream bis = null;
try {
socket = new Socket("localhost", 10086);
System.out.println(socket.getLocalAddress() + ":" + socket.getLocalPort());
//建立好连接之后要发送数据
// 从已经建立好连接的Socket对象中,获取传输数据的输出流
OutputStream out = socket.getOutputStream();
BufferedOutputStream bw = new BufferedOutputStream(out);
//创建文件输入流,读取文件内容
FileInputStream fis = new FileInputStream(fileName);
bis = new BufferedInputStream(fis);
int len;
while ((len = bis.read(buffer)) != -1) {
bw.write(buffer, 0, len);
}
//刷新缓冲输出流缓冲区
bw.flush();
//禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列
socket.shutdownOutput();
接收一下服务器端的反馈
InputStream inputStream = socket.getInputStream();
//如果服务器端没有发送反馈,发送端,会卡在这里
buffer = new byte[1024];
len = inputStream.read(buffer);
System.out.println(new String(buffer, 0, len));
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭socket
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
//关闭读取文件的缓冲流
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.cskaoyan.week5.day28;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPReceiver {
//与uploadFileName配合解决文件重名问题
// 给上传的文件编个号相当于(或者只是用该文件编号来解决上传文件的命名冲突问题也可以)
static long fileId = 0L;
public static void main(String[] args) throws IOException {
boolean flag = true;
ServerSocket serverSocket = new ServerSocket(10086);
//ServerSocket 只是接受,连接请求,并建立连接
// accept方法是一个阻塞方法
Socket socket = null;
while (flag) {
socket = serverSocket.accept();
//解决客户端文件重名问题
String uploadFileName = System.currentTimeMillis() + "-" + fileId++;
new UploadReceiverThread(socket, uploadFileName).start();
}
serverSocket.close();
}
}
class UploadReceiverThread extends Thread {
private Socket socket;
private String fileName;
public UploadReceiverThread(Socket socket, String fileName) {
this.socket = socket;
this.fileName = fileName;
}
@Override
public void run() {
//从建立好连接的服务器端(接收端)的Socket对象中拿得到,输入流,准备通过输入流接收数据
InputStream inputStream = null;
byte[] buffer = new byte[1024];
BufferedOutputStream bos = null;
try {
inputStream = socket.getInputStream();
System.out.println(socket.getLocalPort());
BufferedInputStream bis = new BufferedInputStream(inputStream);
//创建文件字节输出流
FileOutputStream fos = new FileOutputStream(fileName + ".jpg");
bos = new BufferedOutputStream(fos);
int len;
// Socket 中的InputStream的read也是一个阻塞方法
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
//刷新缓冲字节流缓冲区
bos.flush();
//发送反馈信息
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件上传完毕".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭写文件内容的缓冲输出流
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
//关闭socket
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
反射
类加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载:通过一个类的全限定名来获取定义此类的二进制字节流在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
- 连接:验证:确保被加载类的正确性;准备:负责为类的静态成员分配内存并设置默认初始化值;解析:将类中的符号引用替换为直接引用
- 初始化:给静态成员变量赋初值,执行静态代码块内容
类加载时机
- 创建类的实例
- 访问类的静态变量
- 调用类的静态方法使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类,会先触发父类的加载
- 直接使用java.exe命令来运行某个主类
类加载器:完成通过一个类的全限定名来获取描述此类的二进制字节流的功能
- Bootstrap ClassLoader 根类加载器 负责Java核心类的加载,JDK中JRE的lib目录下rt.jar
- Extension ClassLoader 扩展类加载器 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
- Sysetm ClassLoader 系统类加载器 负责加载自己定义的Java类
反射技术
反射技术的实质,其实就是帮助我们获取,运行时类型信息,通过访问,Class对象,从而在运行时获取到,Class对象所对应的类的信息
获取Class对象
// 第一种获取Class对象的而方式
Demo1 demo1 = new Demo1();
Class aClass = demo1.getClass();
// 通过类的字面值常量,获取类对应的字节码文件对象,触发的是一个不完整的类加载过程,不加载static方法
Class demo1Class = Demo1.class;
//通过Class类的静态方法获取字节码文件对象,是一个完整的类加载过程
Class demo1 = Class.forName("com.cskaoyan.reflection.getclass.Demo1");
在不修改代码的情况下改变要加载的类
private static void LoadForConfigFile() throws IOException, ClassNotFoundException {
// 配置文件和Class.forName方法配合,完成在不修改代码的情况下,改变所要加载的类
//1. 此时我们加载的类的全类名,被放在配置文件中,所以我们首先要从,配置文件中,读取到要加载的类的全类名
// 一个容器,配文件的内容,就可以放在这个容器中
// Properties对象中的一个存储单元 一个配置:属性名 属性值
Properties properties = new Properties();
//通过流,读取配置文件内容,配置文件内容,就会被自动存储到我们的Properties对象中
FileInputStream fis = new FileInputStream("config.properties");
//properties对象,有个一个load方法,通过这个load方法,就可以通过流,将配置文件内容读取到Properties对象中,并保存
properties.load(fis);
//从已经读取到配置文件内容,并保存的,Properties对象中,获取我们想要的属性值
String className = properties.getProperty("className");
//根据配置文件的配置完成类加载
Class cls = Class.forName(className);
System.out.println(cls);
}
通过反射获取类中定义的多个构造方法
Constructor[] getConstructors()
//只会返回当Class对象所表示的类中,定义的public访问权限的构造方法
Constructor[] getDeclaredConstructors()
// 返回当前Class对象所表示的类中,定义的所有构造方法
获取指定的单个构造方法
获取单个指定的构造方法:
前提:
1. 区分一个类中,定义的构造方法,通过构造方法的参数列表来区分不同的构造方法,所以,在获取单个构造方法的时候,我们必须指明,构造方法的 参数列表
2. 数据类型... 这种方法参数,可变参数, 所谓可变参数,就是说该类型的参数对应的参数个数是可变的
//可以获取,当前Class对象,所表示的类中,定义的具有指定参数列表的 public 构造方法
// parameterTypes,表示我们要获取的那个构造方法的参数列表,该参数列,表使用数据类型对应字节码文件对象表示
Constructor getConstructor(Class... parameterTypes)
// 可以获取,当前Class对象,所表示的类中, 定义的任意构造方法
Constructor getDeclaredConstructor(Class... parameterTypes)
Constructor privateConstructor = clz.getDeclaredConstructor(int.class);
//获取了clz对象的int类型单参私有构造方法
使用获取的构造方法创建对象
newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
暴力破解
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
Constructor privateConstructor = clz.getDeclaredConstructor(int.class);
//暴力破解权限问题, 一旦使用setAccessble并且参数值传true,该方法就可以让jvm在运行的时候绕过权限检查机制,不在检查权限
privateConstructor.setAccessible(true);
TestGetConstructor o = (TestGetConstructor) privateConstructor.newInstance(100);
System.out.println(o.intValue);
class TestGetConstructor {
public int intValue;
public double doubleValue;
public boolean booleanValue;
//私有权限的一参构造方法
private TestGetConstructor(int intValue) {
this.intValue = intValue;
}
//默认权限的,无参构造方法
TestGetConstructor() {}
/*
受保护的访问权限的,两参构造
*/
protected TestGetConstructor(double doubleValue, boolean booleanValue) {
this.doubleValue = doubleValue;
this.booleanValue = booleanValue;
}
/*
public 访问权限的3参构造
*/
public TestGetConstructor(int intValue, double doubleValue, boolean booleanValue) {
this.intValue = intValue;
this.doubleValue = doubleValue;
this.booleanValue = booleanValue;
}
}
具有可变参数的方法
private static void testVararg() {
//调用具有可变参数的方法
//testMehotdVariable();
testMehotdVariable(1, 2, 3, 4);
//testMehotdVariable(20);
}
// 演示可变参数的含义
// 注意事项: 一个方法的可变参数,只能在方法参数列表的最后一个位置,这意味着一个方法的参数中,只能最多定义一个可变参数
public static void testMehotdVariable(int... a) {
// 可变参数的实质,对应一个数组
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
System.out.println("hello variable");
}
通过反射获取成员变量
前提:Field类用来描述所有的成员变量(包括静态成员变量),一个Field对象表示类中定义的一个成员变量
Field[] getFields() 获取当前Class对象,所表示的类中,定义的 public 成员变量,同时 还可以通过该方法,获取到该类的父类中,定义public成员变量
Field[] getDeclaredFields() 获取当前Class对象,所表示的类中定义的所有成员变量, 但是不包括父类中定义的成员变量
public class Demo1 {
public static void main(String[] args) {
//拿到子类对应的字节码文件
Class sonClass = Son.class;
// Field[] getFields()
Field[] fields = sonClass.getFields();
System.out.println(Arrays.toString(fields));
// Field[] getDeclaredFields()
Field[] declaredFields = sonClass.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
}
}
class Father {
//父类中定义的public成员变量
public int i;
double fatherJ;
}
class Son extends Father {
public int i;
double j;
protected boolean k;
private String name;
}