[Java笔记]day28

day28

review

  1. InetAdderss实例化方法?常用方法?
  • 实例化:不提供公共的构造器,只能通过静态方法实例化

    • InetAddress.getByName(String host):host可以是IP地址或者域名
    • InetAddress.getLocalHost():获取本地IP
  • 常用方法:

    • getHostName():获得域名
    • getHostAddress():获得IP地址
  1. TCP和UDP的区别
  • TCP

    • 可靠的数据传输(三次握手)
    • 可以进行大数据量的传输
    • 效率
  • UDP

    • 不可靠的数据传输
    • 以数据报文的形式传输,限定大小为64kb
    • 效率
  1. 序列化的条件
  • 实现Serializable接口
  • 对象所在的类提供一个静态常量serialVersionUID
  • 对象的属性也必须是可序列化的

反射概述

  1. Java的反射机制

Java的反射(reflection)机制是指在程序的运行状态中,

  • 可以构造任意一个类的对象
  • 可以了解任意一个对象所属的类
  • 可以了解任意一个类的成员变量和方法
  • 可以调用任意一个对象的属性和方法。

在这里插入图片描述

  1. 静态语言和动态语言

在这里插入图片描述

疑问:

通过直接new的方式或反射的方式都可以调用公共结构,开发中用哪个?

反射机制是否与面向对象中的封装性矛盾?

Class

  1. java.lang.Class本身是一个类
  2. 类的加载过程
  • 源程序经过javac.exe编译后会生成一个或多个.class(字节码)文件
  • java.exe对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载,此加载到内存中的类叫做运行时类,此运行时类作为Class类的一个实例

换言之,Class的实例对应着一个运行时类

  1. 获取Class的实例

加载到内存中的运行时类,会缓存一定时间,在此时间段内,可以通过不同方式获取此运行时类

  • 调用运行时类的属性.class
  • 通过运行时类的对象调用getClass()
  • 调用Class静态方法:forName(String className)
  • 使用类的加载器ClassLoader

举例

public class ReflectionTest{
    @Test
    public void test() {
        // .class
        Class<Person> clazz1 = Person.class;
        // getClass()
        Person p1 = new Person();
        Class<? extends Person> clazz2 = p1.getClass();
        // Class.forName()  ---> 最常用
        Class<?> clazz3 = Class.forName("com.yanchengxu.java.Person");
        // ClassLoader       
        ClassLoader loader = ReflectionTest.class.getClassLoader();
        Class<?> clazz4 = loader.loadClass("com.yanchengxu.java.Person");
	}    
}
  1. 可以有Class对象的类型
  • class:类
  • interface:接口
  • []:数组
  • emum:枚举
  • annotation:注解
  • void
  • 基本数据类型

注意
只要数组的元素类型维度相同,即可认为是同一个Class,换言之,与数组长度无关

例如

int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass() == b.getClass());  // true

类的加载与ClassLoader

  1. 类的加载过程

在这里插入图片描述

在这里插入图片描述

  • 什么时候会发生类的初始化?

在这里插入图片描述

  1. ClassLoader

在这里插入图片描述
在这里插入图片描述

  • 举例:使用ClassLoader加载配置文件
public class ClassLoaderTest {
    @Test
    public void testClassLoader() {
        InputStream is = null;
        try {
            // 1. 实例化Properties对象
            Properties properties = new Properties();
            // 2. 获取InputStream
            
            // try1:直接获取:默认识别为Moudule下
            // is = new FileInputStream("src\\test.properties");
            
            // try2:通过ClassLoader获取:默认识别为Module/src下
            ClassLoader loader = ClassLoaderTest.class.getClassLoader();
            is = loader.getResourceAsStream("test.properties");
            // 3. 调用Properties对象的load(InputStream is)方法记载属性
            properties.load(is);
            // 4. 调用Properties对象的getProperty(String key)获取属性值
            String name = properties.getProperty("name");
            String age = properties.getProperty("age");
            // 5. 数据操作
            System.out.println("name = " + name + ", age = " + age);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6. 资源关闭
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

获取运行时类的对象

  1. newInstance()主要用于创建运行时类的对象。方法内部调用的是运行时类的空参构造器

  2. 使用要求

  • 运行时类必须提供空参构造器
  • 此空参构造器要有一定的访问权限,通常设置为public

拓展:JavaBean中要求提供一个空参构造器的原因?

  1. 便于通过反射机制创建运行时类的对象
  2. 便于子类继承此运行时类,且默认调用空参super()时,保证父类中有空参构造器
  1. 使用举例
@Test
public void testNewInstance() throws IllegalAccessException, InstantiationException {
    // 1. 通过.class获取Class实例
    Class<Person> cls = Person.class;
    // 2. 调用newInstrance()创建运行时类的对象
    Person person = cls.newInstance();
    System.out.println(person);
}

获取运行时类的完整结构

  1. 获取运行时类的属性
  • 常用方法
方法原型说明注意
public Field[] getFields()获取所有属性返回当前运行时类及所有父类中声明为public的属性
public Field[] getDeclaredFields()获取所有属性只返回当前运行时类中的所有属性,不包括父类中的属性
public int getModifiers()获取属性的权限修饰符返回值为int,需要用Modifer.toString(int mod)转为字符串,缺省值对应""
public Class getType()获取属性的类型
public String getName()获取属性的名字
  • 应用举例
@Test
public void testField() {
    Class<Person> cls = Person.class;
    Field[] fields = cls.getFields();
    for (Field f : fields) {
        System.out.println(f);
    }
    System.out.println("------------------");
    Field[] fields1 = cls.getDeclaredFields();
    for (Field f : fields1) {
        // 权限修饰符
        int mod = f.getModifiers();
        String smod = Modifier.toString(mod);  // 转为字符串
        // 数据类型
        Class<?> type = f.getType();
        String stype = type.getName();
        // 变量名
        String sname = f.getName();
        System.out.println("mod:" + smod + ", type:" + type + ", name:" + sname) ;
    }
}
  1. 获取运行时类的方法
  • 常用方法
方法原型说明注意
public Method[] getMethods()获取所有方法返回当前运行时类及所有父类中声明为public的方法
public Field[] getDeclaredMethods()获取所有属性只返回当前运行时类中所有方法,不包括父类中的方法
public Annotation[] getAnnotations()获取方法的注解返回数组
public int getModifiers()获取方法的权限修饰符返回值为int,需要用Modifer.toString(int mod)转为字符串,缺省值对应""
public Class getReturnType()获取方法的返回值类型
public String getName()获取方法名
public Parameter[] getParameters()获取方法的参数列表返回数组
public Class[] getExceptionTypes()获取方法的异常返回数组
  1. 获取运行时类的构造器
  • 常用方法
方法原型说明
public Constructor[] getConstructors()获取当前运行时类声明为public的构造器
public Constructor[] getDeclaredConstructors()获取前运行时类中所有的构造器
  1. 获取运行时类的其他结构
  • 获取运行时类的父类/带泛型的父类/带泛型的父类的泛型
@Test
public void testSuperClass() {
    Class<Person> cls = Person.class;
    // 1. 获取父类
    Class<? super Person> superclass = cls.getSuperclass();
    System.out.println(superclass.toString());
    // 2. 获取带泛型的父类
    Type genericSuperclass = cls.getGenericSuperclass();
    System.out.println(genericSuperclass.toString());
    // 3. 获取带泛型的父类的泛型
    ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    System.out.println(actualTypeArguments[0].getTypeName());
}
  • 获取运行时类的接口
@Test
public void testInterface() {
    Class<Person> cls = Person.class;
    Class<?>[] interfaces = cls.getInterfaces();
    for (Class c : interfaces) {
        System.out.println(c.getName());
    }
}
  • 获取运行时类所在的包
@Test
public void testPackage() {
    Class<Person> cls = Person.class;
    Package pack = cls.getPackage();
    System.out.println(pack.getName());
}
  • 获取运行时类的注解
@Test
public void testAnnotation() {
    Class<Person> cls = Person.class;
    Annotation[] annotations = cls.getAnnotations();
    for (Annotation a : annotations) {
        System.out.println(a);
    }
}

获取运行时类的指定结构

  1. 获取运行时类的指定属性
  • 常用方法
方法原型说明注意
public Field getField(String name)获取name对应的属性返回当前运行时类及所有父类中声明为public的属性
public Field getDeclaredField(String name)获取name对应的属性只返回当前运行时类中的所有属性,不包括父类中的属性
public void setAccessible(boolean flag)设置当前属性是否可以访问flag赋值为true时,无视属性的权限修饰符;否则按封装性处理,调用者是Field对象
public void set(Object obj, Object value)设置obj对象的Field属性的值调用者是Field对象
  • 举例
@Test
public void testField() {
    try {
        // 1. 创建运行时类的对象
        Class<Person> cls = Person.class;
        Person person = cls.newInstance();
        // 2. 获取运行时类中指定变量名的属性
        Field name = cls.getDeclaredField("name");
        // 3. 保证属性可以访问
        name.setAccessible(true);
        // 4. 设置属性值
        name.set(person, "Tom");
        System.out.println(person);
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
}
  1. 获取运行时类的指定方法
  • 常用方法
方法原型说明注意
public Method getDeclaredMethod(String name, Class… parameterTypes)获取指定形参的方法返回当前运行时类及所有父类中声明为public的方法
public Method getMethod(String name, Class… parameterTypes)获取指定形参的方法只返回当前运行时类中的指定属性,不包括父类中的方法
public void setAccessible(boolean flag)设置当前方法是否可以访问flag赋值为true时,无视属性的权限修饰符;否则按封装性处理,调用者是Method对象
public Object invoke(Object obj, Object… args)调用obj的方法,实参为args调用静态方法时,obj可以传入null或者当前运行时类的Class对象;调用者是Method对象
  • 举例
@Test
public void testMethod() {
    // 1. 创建运行时类的对象
    Class<Person> cls = Person.class;
    Person person = null;
    try {
        person = cls.newInstance();
        // 2. 获取某个指定的方法
        Method show = cls.getDeclaredMethod("show", String.class);
        // 3. 保证方法可以访问
        show.setAccessible(true);
        // 4. 调用invoke(),可以接收返回值
        Object returnValue = show.invoke(person, "CHN");
        System.out.println(returnValue);
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    System.out.println("------------static method-------------");
    try {
        Method showDesc = cls.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        // 参数也可以写null
        Object retVal = showDesc.invoke(Person.class);
        System.out.println(retVal);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}
  1. 获取运行时类的指定构造器

开发中较少使用,因为指定参数的构造器的通用性较差

@Test
public void testConstructor() {
    try {
        Class<Person> cls = Person.class;
        Constructor<Person> constructor = cls.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Person tom = constructor.newInstance("Tom");
        System.out.println(tom);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页