on java 8 摘录(反射)
面向对象编程的一个基本目标就是,让编写的代码只操纵基类(本例中为
Shape
)的引用
Shape
接口中的方法draw()
是可以动态绑定的,因此客户程序员可以通过泛化的Shape
引用来调用具体的draw()
方法。在所有子类中,draw()
都被重写,并且因为它是一个动态绑定的方法,即使通过泛化的Shape
引用来调用它,也会产生正确的行为。这就是多态。abstract class Shape { void draw() { System.out.println(this + ".draw()"); } @Override public abstract String toString(); } class Circle extends Shape { @Override public String toString() { return "Circle"; } } class Square extends Shape { @Override public String toString() { return "Square"; } } class Triangle extends Shape { @Override public String toString() { return "Triangle"; } } public class Shapes { public static void main(String[] args) { Stream.of( new Circle(), new Square(), new Triangle()) .forEach(Shape::draw); } }
这就是反射的意思:在运行时,确定对象的类型。
当程序第一次引用该类的静态成员时,就会触发这个类的加载。构造器是类的一个静态方法,尽管没有明确使用
static
关键字。因此,使用new
操作符创建类的新对象也算作对该类静态成员的引用,构造器的初次使用会导致该类的加载。
一旦该类型的
Class
对象加载到内存中,它就会用于创建该类型的所有对象。
// reflection/SweetShop.java // 检查类加载器的工作方式 class Cookie { static { System.out.println("Loading Cookie"); } } class Gum { static { System.out.println("Loading Gum"); } } class Candy { static { System.out.println("Loading Candy"); } } public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { System.out.println("Couldn't find Gum"); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); } } /* 输出: inside main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie */
我相信最开始肯定也有人和我一样没看懂为什么这个可以证明,其实是这样的,我们通过观察,可以发现上面类的顺序是Cookie,Gum,Candy这样的顺序下来的,而在下面我最先创建的是Candy,然后Gum,最后Cookie,然后又因为静态方法是类第一次被加载时会出现,那么按照常理,如果它们是提前编译好的,而不是在动态加载的话,它们的输出应该是乱的,或者就是按照编写顺序下来的,我说乱是因为提前全部加载的话,静态代码块一定会一起出现,但是会根据编译器决定哪个加载的块,这样就会导致顺序出问题;综上,既然它是按照主程序的顺序下来,那么肯定就只能是动态加载了.
newInstance()
在Java 8中还是正常的,但在更高版本中已被弃用,Java推荐使用Constructor.newInstance()
来代替。示例中我们使用了@SuppressWarnings("deprecation")
来抑制那些更高版本的弃用警告。
这个可以这么写
Class up = c.getSuperclass();
Object obj = null;
try {
// 对应类要有public的无参构造器:
obj = up.getConstructor().newInstance();
} catch(Exception e) {
throw new RuntimeException("Cannot instantiate");
}
on java 8 摘录(反射)(没看懂的东西)
- 事实上,
Class
对象被用来创建类的所有“常规”对象。
这句话我没太看懂,什么叫做常规对象?
我们对
forName()
的调用只是为了它的副作用:如果类Gum
尚未加载,则加载它。在加载过程中,会执行Gum
的静态代码块。
19.2 Class
对象
-
Class对象有点像静态对象的感觉,它们只被加载一次,后面都是对它的引用
-
有关于Class.forName(),这个写的挺详细的https://www.geeksforgeeks.org/class-forname-method-in-java-with-examples/,然后类名.getclass(),两个调用的方式不一样.
-
我魔改了on java 程序
package test; class Cookie { static { System.out.println("Loading Cookie"); } } class Gum { static { System.out.println("Loading Gum"); } } class Candy { static { System.out.println("Loading Candy"); } } public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); try { Class.forName("Candy"); }catch (ClassNotFoundException e){ e.printStackTrace(); } System.out.println("After creating Candy"); try { Class.forName("test.Gum"); }catch (ClassNotFoundException e){ e.printStackTrace(); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); } }
其他
- toString()方法存在是因为我们需要类的字符串信息,如果没有这个方法的话,你的实例对象传出去的是它的地址
- 类的静态成员只会被加载一次,静态代码块也是一样
- 在idea中注意下,找类的话不是用的绝对地址而是相对地址.针对我上面给出的那个代码
- 看了反射,最大的感觉就是它可以无视具体的类编程,主要框架适用就好