今天的主要内容有:UDP编程:发送数据包和接受数据包;反射:类对象的三种获取方式,通过反射获取构造方法,属性,方法并使用。
Day34
*UDP编程:发送者和接受者,两者直接不会产生连接。(不安全的,在信息传输过程中可能会发生数据包的丢失)
package com.qianfeng.am.demo1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* @author 吴一凡
* UDP里的发送者
*/
public class TestUDPSend {
public static void main(String[] args) {
try {
//·1.构建一个UDPsocket对象,发射器
DatagramSocket ds = new DatagramSocket();
//·需要发送的信息
String msg = "大仙的头发呢?";
//·2.构建一个数据包
//·两个参数:第一个是发送消息的字节数组,第二个是发送消息的长度
DatagramPacket dp = new DatagramPacket(msg.getBytes(), msg.getBytes().length);
//·给数据包设定地址
dp.setSocketAddress(new InetSocketAddress("129.28.161.", 7777));
//·3发送这个数据包,用发射器发射数据包
ds.send(dp);
//·4.关闭udp对象
ds.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.qianfeng.am.demo1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author 吴一凡
* UDPb的接受者
*/
public class TestUDPreceive {
public static void main(String[] args) {
try {
//·1.构建一个UDP对象,指定一个端口,接收器
DatagramSocket ds = new DatagramSocket(7777);
//·2构建一个接受数据的数据包
byte[] bts = new byte[1024*10];
//·数据包对象存不了信息,而是存到字节数组中,0是起始位置
DatagramPacket dp = new DatagramPacket(bts,0,bts.length);
//·3.读取数据包里面的内容,在没有接收到数据时,这里会阻塞等待接受数据
ds.receive(dp);//·用接收器接受发送端传过来的信息,放到数据包中并保存在字节数组中
//·4读取数据,这里用dp.getLength代表接受到的数据的长度
String msg = new String(bts, 0, dp.getLength());
//·5关闭udp
ds.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
*反射: 通过.class来得到对象,属性,方法的一种手段
- 类对象和它的三种获取方式:类对象就是类加载时的产物(.class文件),封装了一个类的所有信息(类名,父类,接口,属性,方法,构造方法)
public class Person {
}
public class Test {
//·单例模式:程序运行期间,Person这个类只有一个对象
public static void main(String[] args) throws ClassNotFoundException {
//·正常获得对象的方式
Person p = new Person();
//·三种获取Person的类对象
//·方法1:通过对象.getClass() 来获取
Class c1 = p.getClass();
//·方法2:通过类名.class来获取
Class c2 = Person.class;
//·方法3:通过静态方法来获取
Class c3 = Class.forName("com.qianfeng.am.demo2.Person");
}
}
- 通过反射获取构造器并且创建对象。
import java.lang.reflect.Constructor;
/**
* @author 吴一凡
* 通过.class对象获取类的构造器
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
//·获取所有公开的构造器
Constructor[] ct = c.getConstructors();
//·获取所有的构造器(全部都能拿到)
Constructor[] ct1 = c.getDeclaredConstructors();
}
}
package com.qianfeng.pm.demo1;
import java.lang.reflect.Constructor;
/**
* @author 吴一凡
* 通过获取构造器来创建对象
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
//·获取公共的无参构造
Person p1 = (Person) c.newInstance();//·默认用无参构造器创建对象
//·获取无参的构造器
Constructor constructor1 = c.getConstructor();
Person p2 = (Person) constructor1.newInstance();
//·获取带有参数的构造器
Constructor constructor2 = c.getConstructor(int.class);
Person p3 = (Person) constructor2.newInstance(6);//·这里的实参必须和获取构造器时的数据类型一致
}
}
package com.qianfeng.pm.demo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author 吴一凡
* 获取私有的构造器,并且能够实例化对象
*/
public class Demo3 {
public static void main(String[] args) {
Class c = Person.class;
try {
//·得到私有的构造器
Constructor dc = c.getDeclaredConstructor(String.class);
//·在JVM的底层如果一个方法、属性、构造器是私有的,访问开关的boolean值为false
//·而这里就是强制将这个开关改为true,这样就可以访问私有的了。
dc.setAccessible(true);
Person p = (Person) dc.newInstance("jack");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 通过反射获取属性,并且修改对象的属性的
package com.qianfeng.pm.demo2;
public class Person {
public String name;
private int age = 20;
char sex;
protected int id;
}
package com.qianfeng.pm.demo2;
import java.lang.reflect.Field;
/**
* @author 吴一凡
* 通过反射来获取所有属性
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
//·根据属性的名称,来获取这个属性对象
Field field = c.getField("name");//·只能获取到public修饰的属性
Field field1 = c.getDeclaredField("age");//·不管这个属性是什么修饰的都能访问到
//·获取所有被public修饰的属性
Field[] fields = c.getFields();
//·获取所有的属性
Field[] fields1 = c.getDeclaredFields();
}
}
package com.qianfeng.pm.demo2;
import java.lang.reflect.Field;
/**
* @author 吴一凡
* 修改私有属性的值,访问一个私有属性
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
//·1 得到这个属性
Class c = Person.class;
//得到一个属性对象
Field f = c.getDeclaredField("age");
//·破解私有属性的开关
f.setAccessible(true);
//·必须用对象才能访问这个对象的属性
Person p = (Person) c.newInstance();
//·使用属性对象调用方法获得实参对象的该属性的值
int p_Age = f.getInt(p);//·通过f对象调用getInt方法(实参为p),来获取p的age属性的值
System.out.println(p_Age);
//对这个对象的属性的值进行修改
f.setInt(p, 100);
Object obj = f.get(p);
System.out.println(obj);
}
}
- 通过反射获取方法,并且调用对象的属性
package com.qianfeng.pm.demo3;
public class Person {
public void test1() {
System.out.println("test1");
}
protected void test2() {
System.out.println("test2");
}
void test3() {
System.out.println("test3");
}
private void test4() {
System.out.println("test4");
}
}
package com.qianfeng.pm.demo3;
import java.lang.reflect.Method;
/**
* @author 吴一凡
* 通过反射获取方法
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
//·根据方法名获取public修饰的方法
Method m1 = c.getMethod("test1");
//·根据方法名来获取任意一个方法
Method m2 = c.getMethod("test2");
//·获取所有公开的方法
Method[] me1 = c.getMethods();
//·获取所有的方法
Method[] me2 = c.getDeclaredMethods();
}
}
package com.qianfeng.pm.demo3;
import java.lang.reflect.Method;
/**
* @author 吴一凡
* 调用通过反射获取到的方法
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
//·获取到了一个私有方法
Method m1 = c.getDeclaredMethod("test4");
//·如果调用这个方法?必须通过对象调用
Object obj = c.newInstance();//·调用默认的无参构造创建对象
//·因为是私有方法,所以必须打开访问权限
m1.setAccessible(true);
//·用invoke方法去执行obj对象的方法
m1.invoke(obj);
//·如果获取的方法有返回值,有参数怎么办?
//·首先在获取方法时,形参列表要一致
Method m2 = c.getDeclaredMethod("add",int.class,int.class);
m2.setAccessible(true);
//·其次调用invoke方法时传参,并接受返回值(用object类型接受,多态)
Object result = m2.invoke(obj,10,20);
System.out.println(result);
}
}
*反射的两个使用案例
- 案例1:通过反射,获取报名,并且获取这个包下面所有的.class文件
import java.io.File;
import java.io.FileFilter;
public class Demo1 {
public static void main(String[] args) {
test(Demo1.class);
}
//·找到c下面同包的所有.class文件
public static void test(Class c) {
//·获取这个字节码文件所在包的包对象
Package package1 = c.getPackage();
//·获取这个包的包名
String pkName = package1.getName();
// System.out.println(pkName);
/*
* 此时pkName = com.qianfeng.pm.demo4
* 需要改成 bin\\com\\qianfeng\\pm\\demo4
*/
pkName = "bin\\"+pkName.replace(".", "\\");
//·得到一个file对象
File file = new File(pkName);
//·得到以.class结尾的文件
File[] lf = file.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if(file.getName().endsWith(".class")) {
if(file.isFile()) {
return true;
}
}
return false;
}
});
for (File file2 : lf) {
System.out.println(file2.getName());
}
}
}
- 案例2:在不知道数组类型的情况下,对数组进行扩容
import java.lang.reflect.Array;
public class Demo2 {
public static void main(String[] args) {
String[] a = {"a","b","c"};
String[] b = (String[]) copyArrays(a, 5);
for (String string : b) {
System.out.println(string);
}
}
public static Object copyArrays(Object obj, int newLength) {
Class cls = obj.getClass();
//·判断是不是数组
if(cls.isArray()) {
//·获取这个数组的类型的类对象
Class type = cls.getComponentType();
//·根据这个类型和传进来的新长度创建一个新数组
Object newArray = Array.newInstance(type, newLength);
//·将旧数组的元素,copy到新数组中去
System.arraycopy(obj, 0, newArray, 0, Array.getLength(obj));
return newArray;
}
return null;
}
}