JavaSE核心API--Java反射机制与Object类

java.lang.reflect.Method

1)理论讲解:可变长参数(JDK1.5之后推出的新特性)

代码演示:

public static void main(String[] args) {
    dosome(1,1.1);
    dosome(1,1.1,"a");
    dosome(1,1.1,"a","b");
    dosome(1,1.1,"a","b","c");
}

public static void dosome(int a,double d,String... s) {
    System.out.println("lenth:"+s.length);
    System.out.println(Arrays.toString(s));
}

2)附件:用于测试反射的类

public class Person {
   public void sayHello() {
        System.out.println("大家好!");
   }

   public void sayHi() {
        System.out.println("hi!");
   }

   public void sayName(String name) {
        System.out.println("大家好!我是"+name);
   }

   public void say(String name,int age) {
        System.out.println("大家好!我是"+name+",今年"+age+"岁了!");
   }
}

3)理论讲解:反射机制

  反射是java中的动态机制,它允许我们实例化对象,调用方法或属性从原来的编码期确定转为在程序运行期决定
  反射提高了灵活度,但是会降低性能,因此适度使用

  class类
  class类的每一个实例用于表示JVM加载的一个类
  所以我们也称其为"类的类对象"
  JVM加载的每个类都有且只有唯一的一个Class实例与之对应,我们可以获取某个类的类对象
  通过它我们可以得知该类的一切信息(类名,有哪些属性,哪些方法等)
  甚至可以动态实例化这个类的对象,并调用它的属性和方法


  获取一个类的类对象有以下几种方式:
  1.每个类都有一个静态属性:class
  用于获取该类的类对象,当我们确定要获取某个类的类对象时可以用这种方式,但由于通过硬编码调用,所以不灵活
  如:
  Class cls=String.class;
  Class cls=int.class;

  2.调用Class的静态方法:forName(String name)
  该方法要求传入要加载的类的完全限定名
  包名.类名
  如:
  Class cls=Class.forName("java.lang.String")

  3.使用类加载器:ClassLoader

代码演示:

  try {
//      Class cls=Class.forName("java.lang.String");
//      Class cls=Class.forName("reflect.Person");

        /*
         * java.util.ArrayList
         * java.lang.Integer
         * java.io.InputStream
         */
        Scanner scan=new Scanner(System.in);
        System.out.println("请输入要加载的类的名字:");
        String className=scan.nextLine();
        Class cls=Class.forName(className);
        scan.close();

        //获取类名
        String name=cls.getName();
        System.out.println(name);
        //获取所有方法(包括从超类继承的方法)
        Method[] methods=cls.getMethods();
        //获取当前类自己定义的方法(不含从超类继承的)
//      Method[] methods=cls.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

4)理论讲解:通过Class进行实例化

代码演示:

    // 实例化Person
    Person p = new Person();
    System.out.println(p);

    System.out.println("请输入要实例化的类名:");
    String className = new Scanner(System.in).nextLine();

    // 1.加载需要实例化的类的类对象
    Class cls = Class.forName(className);

    // 2.通过类对象快速实例化
    Object obj = cls.newInstance();

    System.out.println(obj);

5)理论讲解:利用反射调用方法

      利用反射
      1.加载类对象
      2.通过类对象实例化
      3.获取该类定义的需要调用的方法
      4.执行该方法

代码演示:

    Person p = new Person();
    p.sayHello();

    /*
     * 类名和方法名都可以写入xml文档中,程序运行的时候去读取xml文件即可
     */
    Scanner scan = new Scanner(System.in);
    System.out.println("请输入要实例化的类:");
    String className = scan.nextLine();
    System.out.println("请输入要执行的方法:");
    String methodName = scan.nextLine();
    scan.close();

    /*
     * 1.加载类对象
     */
    Class cls = Class.forName(className);
    /*
     * 2.通过类对象进行实例化
     */
    Object obj = cls.newInstance();
    /*
     * 3.获取要调用的方法 Method的每一个实例用于表示一个类中定义的一个方法
     */
    Method method = cls.getDeclaredMethod(methodName);
    /*
     * 4.调用该方法
     */
    method.invoke(obj);

6)理论讲解:利用反射调用有参数的方法

代码演示:

    Person p = new Person();
    p.sayName("杰克");

    // 利用反射
    Class cls = Class.forName("reflect.Person");
    Object obj = cls.newInstance();
    /*
     * sayName(String)
     */
//  Method method=cls.getDeclaredMethod("sayName",String.class);
//  method.invoke(obj, "汤姆");

    /*
     * sayName(String,int)
     */
    Method method = cls.getDeclaredMethod("say", String.class, int.class);
    method.invoke(obj, "杰森", 15);

补充:Object类

测试类

public class Person {

   private int age;

// public Person(int age) {
//   super();
//   this.age = age;
// }

   public int getAge() {
        return age;
   }

   public void setAge(int age) {
        if (age < 0 || age > 100) {
           return;
        }
        this.age = age;
   }

}

使用当前类测试Object常用方法

public class Point {

   /**
     * 特征/属性-----值不一样为变量,值都一样为常量 
     * 行为/方法---传参的值不同,返回的结果也不一样
     */
   private int x;
   private int y;

   public Point(int x, int y) {
        super();
        this.x = x;
        this.y = y;
   }

   public int getX() {
        return x;
   }

   public void setX(int x) {
        this.x = x;
   }

   public int getY() {
        return y;
   }

   public void setY(int y) {
        this.y = y;
   }

   /**
     * 重写toString方法 toString方法是一个非常常用的方法,很多API的操作都会间接调用该方法
     * 方法的目的是将当前对象转换为字符串,具体返回的字符串格式没有固定要求
     * 遵循的原则是返回的内容中包含当前对象的属性信息,可以通过该字符串的内容直观反应当前对象的内容
     */
   public String toString() {
        String str = "(" + x + "," + y + ")";
        return str;
   }

   /**
     * Object定义的equals方法的设计意图是比较两个对象的内容是否相同
     * 如果不重写该方法,那么Object内部是用"=="比较的,这样就失去量equals比较的意义了 
     * 所以当我们需要调用一个类的equals时,该方法就应当重写
     * 注:java提供的类大部分都已经重写过了,只有我们自定义的类需要自行重写
     * 
     * p.equals this:p o
     * 
     */
   public boolean equals(Object o) {
        if (o == null) {
           return false;
        }
        if (this == o) {
           return true;
        }
        if (o instanceof Point) {
           Point p = (Point) o;
           return this.x == p.x && this.y == p.y;
        }
        return false;
   }

}

理论讲解:

      Object的toString方法的设计意图就是将当前类的实例对象转换为一个字符串
      Object已经实现了toString方法,默认返回当前对象的句柄(类名@地址) 
      但实际上对我们的开发没有什么帮助,所以通常我们会重写这个方法
      注:java定义的类都已经实现了toString方法,只有我们自己定义的类若需要使用该方法,要自行重写

代码演示:

//  Person p=new Person();
//  p.setAge(555);
//  System.out.println(p.getAge());

    Point p = new Point(1, 2);

    String str = p.toString();
    System.out.println(str);// 结果输出为对象p的引用信息

    System.out.println(p);
    /*
     * 一个引用类型在和字符串连接操作时,也是先调用该引用类型的toString方法将其转换为字符串后才和字符串做连接操作的
     */
    String line = "point:" + p;
    System.out.println(line);

    Point[] arr = { new Point(2, 3), new Point(3, 4), new Point(4, 5) };
    System.out.println(Arrays.toString(arr));

    Point p2 = new Point(1, 2);

    System.out.println("p==p2:" + (p == p2));// false
    System.out.println("equals:" + p.equals(p2));// ?