我的java学习笔记(下)

 

三、java反射

1、反射概述

    类的加载机制中提到过,java虚拟机有一个运行时数据区,这个数据区又被分为方法区、堆和栈等。方法区主要的作用就是存储被装载的类的类型信息,当虚拟机装载某个类型的时候,需要装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class中的类型信息,将这些信息存储到方法区中。[1、这个类的全限定名......11、指向class类的引用]

    Class类是一个非常重要的java基础类,每当装载一个新的类型的时候,java虚拟机都会在java堆中创建一个对应于新类型的Class实例[Class类没有公有的构造方法,它由JVM自动调用],该实例就代表此类型,通过该Class实例我们就可以访问存储在方法区中该类型的基本信息。同一个类型的Class对象全局只有一个,即如果某个类型在内存中已经加载了,就不会重复创建Class对象。

    反射机制的概念:所谓反射机制就是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为java语言的反射机制。通俗的讲:反射使我们可以得到装载到JVM中的类的内部信息,允许我们在执行程序时得到,而不是在编写代码的时候就必须知道所需类的内部信息。我们可以根据类的部分已知信息,来还原类的全部信息。[动态获取类中的信息,就是java反射]

例如:假设对于类ReflectionTest.java,我们知道的唯一信息是它的类名是“com.cn.ReflectionTest”。这时,我们想要知道ReflectionTest.java的其它信息(比如它的构造函数,它的成员变量等等),要怎么办呢?

    这就需要用到“反射”。通过反射,我们可以解析出ReflectionTest.java的完整信息,包括它的构造函数,成员变量,继承关系等等。

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:

Class类:代表一个类。

Field 类:代表类的成员变量(成员变量也称为类的属性)。

Method类:代表类的方法。

Constructor 类:代表类的构造方法。

Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

2、反射获取对象的三种方式

2.1、Class类中的方法Class.forName("类名字符串") 

/**

  * (注:类名字符串必须是全称,包名+类名)这种方式装入类,会做类的静态初始化

  */

Class<?> cls1 = Class.forName("java.lang.String");

System.out.println(cls1);//class java.lang.String

2.2、表达式方式获取[类型.class]

    类型.class:class字面值是一个表达式,其值等于指定类型的Class对象。表达式包括类的名字、接口、数组或基本类型或伪类型void。X.class(X是类、接口、数组或基本类型或伪类型)class字面值的类型是Class<X>。如void.class的类型是Class<void>

所有的引用数据类型(类-类型)的类名、基本数据类型都可以通过.class方式获取其 Class对象。

/**

* 对于基本数据类型的封装类还可以通过.TYPE 的方式获取其 Class 对象,但要注

* 意。TYPE 实际上获取的封装类对应的基本类型的 Class 对象的引用。

* 通过这种方式不会初始化静态域,clazz和clazz1引用地址同

*/

System.out.println(int.class == Integer.class);//false

System.out.println(int.class == Integer.TYPE); //true

Class clazz = Person.class;

Class clazz1 = Person.class;

System.out.println(clazz == clazz1);//true

System.out.println("是否原始类型:"+int.class.isPrimitive());//true

System.out.println("是否原始类型:"+void.class.isPrimitive());//true


//注:基本数据类型,就没有类的全限定名,也没有getClass方法.一般可以用在反射查找具有int参数的方法中

2.3、Object类中的方法:obj.getClass()

 /* *

   * 想要用这种方式,必须要明确具体的类,并创建对象

   * 说明:对类进行静态初始化、非静态初始化;返回引用o运行时真正所指的对象

   * (因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象

   */

String str1 = new String();

String str2 = new String();

Class cls3 = str1.getClass();

Class cls4 = str1.getClass();

System.out.println(cls3 == cls4);//true

注:三者的区别案例

public class ReflectionTest {

public static void main(String[] args) {

try {

// 测试.class

Class testTypeClass = TestClassType.class;

System.out.println("testTypeClass---" + testTypeClass);

// 测试Class.forName()

Class testTypeForName = Class.forName("hls.com.cn.TestClassType");

System.out.println("testTypeForName---" + testTypeForName);

// 测试Object.getClass()

TestClassType testTypeGetClass = new TestClassType();

System.out.println("testTypeGetClass---"+ testTypeGetClass.getClass());

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

 

}

class TestClassType {

// 构造函数

public TestClassType() {

System.out.println("----构造函数---");

}

// 静态的参数初始化

static {

System.out.println("---静态的参数初始化---");

}

// 非静态的参数初始化

{

System.out.println("----非静态的参数初始化---");

}

}

 /*------------------------输出结果:--------------------

  *    testTypeClass---class hls.com.cn.TestClassType

  *    ---静态的参数初始化---

  *    testTypeForName---class hls.com.cn.TestClassType

  *    ----非静态的参数初始化---

  *    ----构造函数---

  *    testTypeGetClass---class hls.com.cn.TestClassType

  *------------------------------------------------------

  */

    根据结果可以发现,三种生成的Class对象一样的。并且程序只打印一次“静态的参数初始化”。

    我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。

    因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。

    所以,生成Class对象的过程其实是如此的:

    在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,类加载器,就会定位到对应的.class文件,然后载入.class文件,并生成一个Class类型的对象。若是装载了,则根据class文件生成实例对象。

总结:

  1. 类型.class

JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象

b)Class.forName(“类全限定名”):

    装入类,并做类的静态初始化,返回Class的对象

c)实例对象.getClass()

   对类进行静态初始化、非静态初始化;返回引用o运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象

3.获取Class中的构造函数

3.1、构造函数相关API

// 获取“参数是parameterTypes”的public的构造函数

public  Constructor   getConstructor(Class[] parameterTypes)

// 获取全部的public的构造函数

public  Constructor[]  getConstructors()

// 获取“参数是parameterTypes”的,并且是类自身声明的构造函数,包含public、protected和private方法。

public  Constructor   getDeclaredConstructor(Class[] parameterTypes)

// 获取类自身声明的全部的构造函数,包含public、protected和private方法。

public  Constructor[]  getDeclaredConstructors()

// 如果这个类是“其它类的构造函数中的内部类”,调用getEnclosingConstructor()就是这个类所在的构造函数;若不存在,返回null。意思就是说如果一个类A的构造函数中定义了一个内部类InnerA,则通过InnerA的Class对象调用getEnclosingConstructor()方法,可以获取类A的这个构造函数

public  Constructor   getEnclosingConstructor()

//案例:

package hls.com.cn;

import java.lang.reflect.Constructor;

public class Person {

Class cls;

    private Gender gender; //性别

    private int age;

    private String name;

    

    public Gender getGender() {

return gender;

}

public void setGender(Gender gender) {

this.gender = gender;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

private Person(){

     this.name = "unknow";

     this.age = 0;

     this.gender= gender.Female;

     System.out.println("这是private Person()");

    }

    protected Person(String name){

     this.name = name;

     this.age = 0;

     this.gender= gender.Female;

     System.out.println("这是protected Person(String name)");

    }

    public Person(String name,int age,Gender gender){

     this.name = name;

     this.age = age;

     this.gender= gender;

     System.out.println("这是 public Person(String name,int age,Gender gender)");

    }

    public Person(String name, int age){

        this.name = name;

        this.age = age;

        this.gender = Gender.Female;

        //内部类在构造方法中

        class InnerA{

        }

        // 获取InnerA的Class对象

        cls = InnerA.class;

}

    private void privateMethod(){

     System.out.println("我是私有方法");

}

    class InnerB{

    }

    @Override

    public String toString() {

        return "("+name+", "+age+", "+gender+")";

    }

}

//枚举类型,性别

enum Gender{

Male,Female

}

 

public class TestReflect {

public static void main(String[] args) {

Class c;

try {

c = Class.forName("hls.com.cn.Person");

//getDeclaredConstructor 

Constructor[]  allCon =  c.getDeclaredConstructors();//获取全部构造包括私有

Constructor ryCon = c.getDeclaredConstructor();//获取无参构造不管什么权限

Class cn = Class.forName("hls.com.cn.Person$InnerB");//内部类的全限定名

Constructor cs2 =  cn.getDeclaredConstructor(hls.com.cn.Person.class);//hls.com.cn.Person$InnerB(hls.com.cn.Person)

//这里的可变参数:可以 new Class[]{String.class} 也可以直接写 String.class

Constructor cst2 = c.getDeclaredConstructor(new Class[]{String.class});//获取带参构造

/**

 * newInstance() 默认返回的是Object对象,而向下转型,可以以实际类型转

 */

Person p2 = (Person) cst2.newInstance("zly");

System.out.println(p2.getName()); //zly

/** setAccessible(true) 并不是将方法的访问权限改成了public,而是取消java

             * 的权限控制检查。所以即使是public方法其accessible 属性只是默认false

 * Field Method Constructor在使用构造器的时候,是会执行检查的(private:

             * 只能在同类中访问)

 * 设置为true,则不进行检查,否则无权限访问,则会报:

             * java.lang.IllegalAccessException

 * not access a member ... modifiers "private"

 * 不管是方法(包括构造方法)、属性跨越访问权限都要设置不执行检查

 */

ryCon.setAccessible(true);

Object p1 =  ryCon.newInstance(); //创建对象 ryCon 的访问限定符是private

//getConstructor

Constructor[] pubCon = c.getConstructors(); //获取全部public构造

Constructor onePubCon1 = c.getConstructor(String.class,int.class); 

 // 根据class,调用Person类中有内部类InnerA的构造函数

            Class cn1 = new Person("zly",25).cls;  //获取内部类的Class对象

            //取得的hls.com.cn.Person(java.lang.String,int)

            Constructor cst1 = cn1.getEnclosingConstructor(); 

} catch (Exception e) {

e.printStackTrace();

}

}

}

3.2、成员方法相关API

// 获取“名称是name,参数是parameterTypes”的public的函数(包括从基类继承的、从接口实现的所有public函数)

public  Method    getMethod(String name, Class[] parameterTypes)

// 获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)

public  Method[]    getMethods()

// 获取“名称是name,参数是parameterTypes”,并且是类自身声明的函数,包含public、protected和private方法。

public  Method     getDeclaredMethod(String name, Class[] parameterTypes)

// 获取全部的类自身声明的函数,包含public、protected和private方法。

public  Method[]    getDeclaredMethods()

// 如果这个类是“其它类中某个方法的内部类”,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。

public  Method     getEnclosingMethod()

//案例:

public class ReflectTest2 {

    public static void main(String[] args) {

try {

    Class c = Class.forName("hls.com.cn.Person");

    Constructor con = c.getDeclaredConstructor(String.class);

    Person p = (Person) con.newInstance("赵丽颖");

    Method[] m1 = c.getMethods(); //获取所的公共成员方法,包括继承的

    Method m2 = c.getMethod("setName",String.class); //获取指定名字参数的类型的方法

    Method m3 = c.getDeclaredMethod("privateMethod");//获取类声明的方法(包括私有)

    m3.setAccessible(true);

    m3.invoke(p);//我是私有方法

    Method[] m4 = c.getDeclaredMethods();

    for (Method m: m4) {

        System.out.println(m);

    }

   } catch (Exception e) {

     e.printStackTrace();

   }

 }

}

3.3、成员变量相关API

// 获取“名称是name”的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量)

public  Field   getField(String name)

// 获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)

public  Field[] getFields()

// 获取“名称是name”,并且是类自身声明的成员变量,包含public、protected和private成员变量。

public  Field   getDeclaredField(String name)

// 获取全部的类自身声明的成员变量,包含public、protected和private成员变量。

public  Field[] getDeclaredFields()

案例:

public class ReflectTest3 {

    public static void main(String[] args) {

       try {

            Class c = Class.forName("hls.com.cn.Person");

            Constructor con = c.getDeclaredConstructor(String.class);

            Person p = (Person) con.newInstance("赵丽颖");

            System.out.println(p);//(赵丽颖,0,Female)

            Field f1 = c.getDeclaredField("name");//private ...Person.name

            f1.setAccessible(true);

            f1.set(p, "zly");

            System.out.println(p);//(zly,0,Female)

            Field[] f2 = c.getDeclaredFields();

            for(Field f: f2){

               System.out.println(f);

            }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

}

4、静态代理和动态代理

静态代理和动态代理概述

    代理: 为被代理对象产生一个代理对象。

    静态代理:被代理类的数量有限,这个代理类只能代理这些已知的被代理类

    动态代理:被代理的类的数量没有限制,可以代理无限多个类

    动态代理又分为JDK动态代理和CGLib动态代理

    JDK动态代理:使用JDK动态代理产生的代理对象只能转换成被代理对象的接口的对象

代理类的创建:

   实现InvocationHandler接口

   ②实现invoke方法

   定义一个Object属性

   定义一个带有Object类型的参数的构造器:用于将被代理对象传递进来赋值给obj

   定义一个方法,用来产生obj的代理对象

   ⑥完善invoke方法

CGLib动态代理:通过创建被代理类的子类来构造一个代理对象。因为CGLib动态代理,依赖于第三方Jar包

   创建一个类实现MethodInterceptor接口

   实现intercept方法

   定义一个Object属性,用来表示被代理对象

   ④定义一个带有object类型参数的构造器,用于将被代理对象传递进来赋值给obj

   ⑤创建一个方法,用于产生代理对象

 

JDK

CGLib

作用

都是用来产生被代理对象代理对象

实现接口

InvocationHandler

MethodInterceptor

实现原理

通过被代理类实现的接口来创建代理对象

通过构造被代理类的子类来创建代理对象

适用的被代理类

可以为实现了接口的类创建代理对象

可以为没被final修饰的类创建代理对象

 

JDKProxy dp2 = new JDKProxy(new BaoLiHouse());

//产生的代理对象只能转换成被代理对象实现的接口

ZhongJie zj = (ZhongJie)dp2.getProxy();

yk.buy();

CGLibDynamicProxy cdp = new CGLibDynamicProxy(new User());

//通过CGLib动态代理产生的代理对象可以强转成被代理对象本身

User pobj = (User)cdp.getProxy();

pobj.print();

JDK动态代理类】

public class JDKDynamicProxy implements InvocationHandler {

   private Object obj;// 表示被代理对象

   public JDKDynamicProxy(Object o) {

      this.obj = o;

   }

 

   public Object getProxy() {

       //第一个参数:被代理对象的类的加载器

        //第二个参数:被代理对象实现的所有接口

      return Proxy.newProxyInstance(obj.getClass().getClassLoader(),

obj .getClass().getInterfaces(), this);

   }

 

   public Object invoke(Object proxy, Method method, Object[] args)

        throws Throwable {

      // 代码1

      Object rv = method.invoke(obj, args);

      // 代码2

      return rv;

   }

}

CGLib动态代理】

public class CGLibDynamicProxy implements MethodInterceptor {

   private Object obj;

   public CGLibDynamicProxy(Object obj){

      this.obj = obj;

   }

  

   public Object getProxy(){

      Enhancer en = new Enhancer();

      en.setSuperclass(obj.getClass());

      en.setCallback(this);

      return en.create();

   }

  

   public Object intercept(Object obl, Method method, Object[] args,

        MethodProxy mp) throws Throwable {

      //代码1

      System.out.println("before");

      Object rv = method.invoke(obj, args);

      System.out.println("after");

      //代码2

      return rv;

   }

}

 

四、IO流

1、File类

1.1、File类构造及其常用方法

    尽管java.io定义的大多数类是实行流式操作的,File类不是。它直接处理文件和文件系统。也就是说,File类没有指定信息怎样从文件读取或向文件存储;它描述了文件本身的属性。File对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径。此外,File还浏览子目录层次结构。很多程序中文件是数据的根源和目标。

 a)File类主要属性

static String

separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。

static String

pathSeparator与系统有关的路径分隔符,为了方便,它被表示为一个字符串。

 b)File类的构造方法

File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

 c)常用方法

   获取:

String

getName() 获取文件或目录的名称。

File

getAbsoluteFile() 获取绝对路径名形式。

String

getAbsolutePath() 获取绝对路径名字符串。

long

length() 返回文件的大小。

long

lastModified() 返回文件最后一次被修改的时间。

创建:

boolean

createNewFile() 和输出流不同,如果文件不存在,则创建,如果文件存在,则不创建。

boolean

delete() 删除此抽象路径名表示的文件或目录(如果删除文件夹,此文件夹必须是空内容)。

void

deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。

boolean

mkdir() 创建此抽象路径名指定的目录。

boolean

mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。

判断:

boolean

exists() 测试此抽象路径名表示的文件或目录是否存在。

boolean

isDirectory() 测试此抽象路径名表示的文件是否是一个目录。

boolean

isFile() 测试此抽象路径名表示的文件是否是一个标准文件。

boolean

isHidden() 测试此抽象路径名指定的文件是否是一个隐藏文件。

 boolean

canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。

 boolean

canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。

重命名:

boolean

renameTo(File dest)
重新命名此抽象路径名表示的文件(其实就是剪切或移动文件)。

根目录和容量获取:

static File[]

listRoots() 列出可用的文件系统根(分区)。

long

getFreeSpace()
返回此抽象路径名指定的分区中未分配的字节数(可用空间)。

long

getTotalSpace()
返回此抽象路径名指定的分区大小。(分区容量)

long

getUsableSpace()
返回此抽象路径名指定的分区上可用于此虚拟机的字节数。

获取目录内容:

String[]

list() 获取当前目录下的文件以及文件夹名,包含隐藏文件。调用list方法的File对象中封转的必须是目录,否则会发生NullPointerException异常,如果访问的系统级目录,也会发生空指针异常。如果目录存在但是没有内容,会返回一个数组,但长度为0.

//获取:

public class TestFile {

    public static void main(String[] args) {

        System.out.println(File.separator);   //-->   \

        System.out.println(File.pathSeparator);//-->  ;  

    /**

     * 第一个File对象是由仅有一个目录路径参数的构造函数生成的。

     * 第二个对象有两个参数——路径和文件名。

     * 第三个File对象的参数包括指向f1文件的路径及文件名。

     * f3和f2指向相同的文件。
    
     */

    File f1 =new File("/");

    File f2 = new File("/"+"autoexe.bat");

    File f3 =new  File(f1,"autoexe.bat");

    System.out.println(f3.getPath());  //相对路径   \autoexe.bat

    System.out.println(f3.getAbsolutePath());//绝对路径   E:\autoexe.bat

    File f4 = new File("E:\\text\\a.txt");

    System.out.println("最后修改时间"+f4.lastModified()+" 大小:"+f4.length());

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    Date date = new Date();

    date.setTime(f4.lastModified());

    System.out.println(sdf.format(date));

  }

}

public class TestFile {

    public static void main(String[] args) throws IOException {

       //---------创建和删除----------//

       File f1 = new File("E:/text/ts"); //只有text目录,无ts目录

       boolean b =f1.mkdirs();

       System.out.println(b);  //true

       //System.out.println(f1.delete()); //true ts目录被删除,delete只能删除空内容的文件夹

       File f2 = new File("E:/text/a.txt");

       boolean b1 = f2.createNewFile(); //没有就会创建一个新的文件,有就不会创建

       //System.out.println(f2.delete());

       //---------判断----------//

       System.out.println("文件或目录是否存在:"+f2.exists()); 

       System.out.println("是否是目录:"+f1.isDirectory());  

       System.out.println("是否是文件:"+f2.isFile());

       System.out.println("是否隐藏:"+f2.isHidden());

       //---------重命名---------//

       File f3 = new File("E:/text/b.txt");

       System.out.println(f2.renameTo(f3)); 

       File file = new File("E:\\");

       String names[] = file.list();//列出路径中的目录和文件

       for (String name : names) {

         System.out.println(name);

       }

    }

}

扩展:java中的转义字符“\”

Java中为什么需要转义字符:转义字符的意义就是避免出现二义性。

例如:

  1. 在路径中“com\cn\hls”,这里面的\你不清楚这是路径的下一层,还是就是指字符反斜杠。
  2. 在“\”中,你不知道这个引号是要作为字符还是作为定义字符串的格式。

Java中的常见转义字符:

符号

意义

\n

回车

\t

水平制表符

\r

换行

\f

换页

\

单引号

\

双引号

\\

反斜杠

 

注:java中斜杠和反斜杠的区别。

在Unix/Linux中的路径分隔符采用斜杠“/”,而反斜杠“\”表示跳脱字符,将特殊字符变成一般字符。

而在Windows中,路径分隔符采用反斜杠“\”,而斜杠“\”表示除法。比如:C:\Windows\System。

但是在编程中,无法直接这么使用,在java语言中一个反斜杠是转义字符,而斜杠就是一个字符,表示地址路径下一级目录,也表示除号。但是也可以这么写:C:/Windows/System。

1.2、过滤器

String[]

list(FilenameFilter filter)
返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穆瑾轩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值