黑马程序员——反射

 ------- android培训java培训、期待与您交流! ----------


Class类
平常的java类是通过一些固定的支架(方法,成员变量等)搭建来的,搭建完成后它就具备了描述一类事物的特性。
而这些支架也属于事物,它也能够被类描述。所以java中就提供了Class类进行描述。
Class类所描述的事物是:java类在编译完成后,会生成class文件存在硬盘上。也就是
字节码。把字节码(保存了该类的基本信息)加载到内存中来。返回的类型是Class,当然
Class就具备了描述该类固定支架(方法,成员变量等)的特性。得到字节码的方法3种:
类名.class  例如,System.class
对象.getClass() 例如,new Date().getClass();
Class.forName(),例如,Class.forName("java.util.Date");
1,如果字节码已经在内存中,直接拿过来就哦了。
2,如果字节码不在内存中,需要从硬盘上把字节码加载到内存中去。
分析Class类。
package reflect;
public class ReflectTest {
 public static void main(String[] args) throws ClassNotFoundException {

  String abc = "afd";
  Class cls1 = abc.getClass();
  Class cls2 = String.class;
  Class cls3 = Class.forName("java.lang.String");
  // 虚拟机上做出该类一份字节码,其他要用就直接拿了。所以打印结果都为true
  System.out.println(cls1 == cls2);
  System.out.println(cls1 == cls3);

  // 判断是否是基本数据类型的字节码isPrimitive()。false
  System.out.println(cls1.isPrimitive());
  // 基本类型的字节码和对应的封装类的字节码是不相同的。false、
  System.out.println(int.class == Integer.class);
  // TYPE常量代表包装类型所包装的基本类型的字节码。true
  System.out.println(int.class == Integer.TYPE);
  // 数组的字节码类型,不是原始类型false。
  System.out.println(int[].class.isPrimitive());
  // 数组的字节码类型,判断一个Class类型是否为数组用isArray()。true
  System.out.println(int[].class.isArray());

  
  // Class类型,判断基本数据类型:isPrimitive();判断数组类型:isArray();
  // 总之:只要是在源程序中出的类型,都有各自的Class
 }

}


反射的概念。
反射就是把Java类中的各种成分映射成相应的java类。
Constructor类
Constructor类代表某个类中的一个构造方法。
//得到构造方法:  通过构造函数参数列表的类型,来确定获取的具体构造类。
  Constructor<String> strCon = String.class.getConstructor(StringBuffer.class);
//  new String(new StringBuffer("abc"));
//创建实例对象:
//  1,在对strCon进行实例化对象的时候,需要指定参数对象。
  String str2 = strCon.newInstance(new StringBuffer("abc"));
  System.out.println(str2.charAt(2));
//  2,Class类提供了一个便利,有newInstance方法。直接可以创建对象。省去了获取Constructor再去创建对象。
//  反射的缺点:反射比较消耗性能占用时间,进行反射以后把得出的结果存在缓存起来。为后面尽可能的减少反射的使用量。
  String s = String.class.newInstance();
 }

Field类

package reflect;

import java.lang.reflect.Field;

public class FieldDemo {
 public static void main(String[] args) throws SecurityException, NoSuchFieldException, InstantiationException, IllegalAccessException {
  ReflectPoint p = new ReflectPoint(3,5);
//  字段通过字段名字区分。它不代表某个特定对象上面的值。而是告诉程序员,这个叫y的变量已经给你取到了。封装成了一个类。
//  类上的变量,不是对象上的变量。不包括私有的。
  Field fieldY = p.getClass().getField("y");
//  从特定对象上取值。这个异常说明了,成员是私有的不让访问。
//  Exception in thread "main" java.lang.NoSuchFieldException: y
//  at java.lang.Class.getField(Class.java:1520)
//  at reflect.FieldDemo.main(FieldDemo.java:10)

  System.out.println(fieldY.get(p));
//  获取所有申明的变量。包括private类型。
  Field fieldX = p.getClass().getDeclaredField("x");
//  java.lang.IllegalAccessException: Class reflect.FieldDemo can not access a member of class reflect.ReflectPoint with modifiers "private"
//  上面异常的出现说明了,没有获取私有变量值的权限。
//  如果想要获取的话,需要进行暴力反射。
  fieldX.setAccessible(true);
  System.out.println(fieldX.get(p));
//  Class c = String.class;
//  String s = (String) c.newInstance();
//  
//  String s1 = String.class.newInstance();
 }
}
将一个对象上所有的String类型的值中含有a的,进行替换。
 private static void changeValue(ReflectPoint p) throws IllegalArgumentException, IllegalAccessException {
// 获取ReflectPoint中所有申明的字段。
  Field[] fs = p.getClass().getDeclaredFields();
//  遍历字段。
  for(Field f:fs){
//   getType获取Field的类型,用==好,因为字节码只有一份。如果是String类型的话,就进行操作。
   if(f.getType()==String.class){
    String oldValue = (String)f.get(p);
    String newValue = oldValue.replace("b", "a");
    f.set(p,newValue);
    System.out.println(newValue);
   }
  }
 }
}
反射的作用:你给我一个对象我能把他里面的值都改掉。例如Spring框架中用的就是这个技术。你给我一个类,我就能用你给的资源来做些事情。。
Method类

// 通过方法名字,和参数列表来确定方法。导包的时候看到com...这种包是内部用的(不要导),看到java...这就是外部用的。
  String str = "abc";
  Method methodCharAt = String.class.getMethod("charAt", int.class);
  
//  已知Method,调用谁的Method并且还要给它传参数。
  Character ch = (Character) methodCharAt.invoke(str, 2);
  System.out.println(ch);
//  静态方法的调用,因为静态方法不需要对象。第一个参数应该是null。所以:
//  Character ch2 = (Character) methodCharAt.invoke(null, 2);
  
  String className= args[0];
  invokeMain(className);
 }
// 专家模式:只要类中有这个私有的数据,对这个数据的操作只能是我这个类的方法。
// 谁享有数据,谁就对这个数据有操作的权利。


调用其他类的main方法。
为什么要用反射的方式调用main。
main的参数是String数组,数组里面可以装类名字符串。
获取这这些类的名字。通过反射的原理可以获得各个类中的main方法。
分别执行这几个类。
 private static void invokeMain(String className) throws Exception {
  Method main = Class.forName(className).getMethod("main", String[].class);
//  main是静态,所以第一个参数是空的。
// java.lang.IllegalArgumentException: wrong number of arguments jdk1.4特点,给进去一个数组会将数组拆掉,拆掉后的值作为参数造成的。导致参数变多了。
  main.invoke(null, new Object[]{new String[]{"1","2","3"}});
//  main.invoke(null, (Object)new String[]{"1","2","3"});
 }

数组的反射。
package reflect;
import java.lang.reflect.Array;
import java.util.Arrays;

public class ArrayReflectDemo {
 public static void main(String[] args) {
  int[] a = new int[3];
  int[] a2 = new int[4];
  int[][] a3 = new int[2][3];
  String[] a4 = new String[3];
  // 数组class是否相同:1.数据类型2数组的维数
  System.out.println(a.getClass() == a2.getClass());
  // 数组的父类是Object
  System.out.println(a4.getClass().getSuperclass().getName());
  // 
  Object aObj1 = a;
  Object aObj2 = a2;
  // 因为a是一维数组,里面装的是int型变量,不是Object子类。不行。
  // Object[] obj1=a;
  // 二维数组,数组里面套数组。内层数组(一维数组)相当于是Object。可以
  Object[] aobj3 = a3;
  // String数组里面装的是String类型的变量。可以
  Object[] aobj4 = a4;
  System.out.println(Arrays.asList(a));
  System.out.println(Arrays.asList(a4));
//  数组的反射。
  
  printObjcet(a4);
 }

 private static void printObjcet(Object obj) {
  Class clazz = obj.getClass();
  if(clazz.isArray()){
//   反射
   int len = Array.getLength(obj);
   for(int i=0;i<len;i++){
    System.out.println(Array.get(obj, i));
   }
  }
 }
}

 

### TCP传输原理与实现 TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议。其核心原理是通过建立端到端的连接,确保数据在不可靠的网络中能够可靠地传输。TCP 使用确认机制、流量控制、拥塞控制等机制来保障数据的完整性和有序性。 在 Java 中,TCP 通信可以通过 `Socket` 和 `ServerSocket` 类实现。客户端使用 `Socket` 连接到服务器,服务器端使用 `ServerSocket` 监听连接请求。通信过程中,数据通过输入流和输出流进行传输。 #### 客户端实现 客户端的主要任务是与服务器建立连接,并通过输出流向服务器发送数据,同时可以通过输入流接收服务器的响应。例如: ```java Socket s = new Socket(InetAddress.getLocalHost(), 10004); OutputStream out = s.getOutputStream(); out.write("Hello TCPClient".getBytes()); InputStream is = s.getInputStream(); byte[] buffer = new byte[1024]; int len = is.read(buffer); System.out.println(new String(buffer, 0, len)); s.close(); ``` 该代码实现了客户端与服务器的连接,并发送了一条文本消息,同时接收服务器的响应[^1]。 #### 服务端实现 服务端通过 `ServerSocket` 监听指定端口,等待客户端连接。一旦连接建立,服务端通过输入流接收客户端发送的数据,并通过输出流向客户端发送响应。例如: ```java ServerSocket ss = new ServerSocket(10012); Socket s = ss.accept(); InputStream in = s.getInputStream(); byte[] buffer = new byte[1024]; int len = in.read(buffer); System.out.println(new String(buffer, 0, len)); PrintWriter out = new PrintWriter(s.getOutputStream(), true); out.println("<font color='red' size=7>客户端s你好</font>"); s.close(); ss.close(); ``` 该代码展示了如何在服务端接收客户端发送的数据,并向客户端发送 HTML 格式的响应[^3]。 #### TCP连接的建立与释放 TCP 连接的建立采用三次握手(Three-way Handshake): 1. 客户端发送 SYN(同步)报文给服务器,表示请求建立连接。 2. 服务器收到 SYN 报文后,发送 SYN-ACK(同步-确认)报文作为响应。 3. 客户端收到 SYN-ACK 后,发送 ACK(确认)报文,连接建立。 连接的释放采用四次挥手(Four-way Handshake): 1. 客户端发送 FIN(结束)报文,表示数据发送完成。 2. 服务器发送 ACK 报文,确认收到 FIN。 3. 服务器发送 FIN 报文,表示数据发送完成。 4. 客户端发送 ACK 报文,连接关闭。 #### TCP的可靠性机制 TCP 通过以下机制确保数据的可靠传输: - **确认机制**:接收方收到数据后,向发送方发送确认信息。 - **重传机制**:如果发送方未收到确认信息,则重传数据。 - **流量控制**:通过滑动窗口机制,控制发送速率,避免接收方缓冲区溢出。 - **拥塞控制**:通过慢启动、拥塞避免等算法,防止网络拥塞。 #### TCP的编程模型 TCP 编程模型通常包括以下几个步骤: 1. **创建 Socket**:客户端创建 `Socket` 对象,连接服务器;服务端创建 `ServerSocket` 对象,监听端口。 2. **获取流对象**:获取 `Socket` 的输入流和输出流,用于数据传输。 3. **数据读写**:通过输入流读取数据,通过输出流写入数据。 4. **关闭连接**:通信结束后,关闭 `Socket` 和流对象。 ### 示例代码:完整的 TCP 通信 以下是一个完整的 TCP 通信示例,包含客户端和服务端的代码。 #### 客户端代码 ```java import java.io.*; import java.net.*; public class TcpClient { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 8888); OutputStream out = socket.getOutputStream(); out.write("Hello Server!".getBytes()); InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = in.read(buffer); System.out.println("Server response: " + new String(buffer, 0, len)); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` #### 服务端代码 ```java import java.io.*; import java.net.*; public class TcpServer { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("Server is listening on port 8888..."); Socket socket = serverSocket.accept(); InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = in.read(buffer); System.out.println("Client message: " + new String(buffer, 0, len)); OutputStream out = socket.getOutputStream(); out.write("Hello Client!".getBytes()); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值