java 类何时会被加载

java类在以上五种情况下会被加载。

在jvm生命周期中每个类如果存在,则不会重复加载。

在加载子类的时候会优先加载其父类。

类被加载的时候,其中的静态代码块、静态方法及静态变量也会被加载。

在初始化某个类时,如果这个类的静态代码块、静态方法或静态变量引用到了另一个类,则这个类也会被加载。
————————————————
版权声明:本文为CSDN博主「瀚忄」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_30233335/article/details/114390832

类的加载顺序

有父子关系的类在加载时
先调用父类静态初始化块->静态属性(不包括静态方法)
然后是子类
子类静态初始化块->静态属性(不包括静态方法)

创建对象的过程

先是父类非静态初始化块非静态属性 ,再是父类构造函数
然后是子类非静态初始化块非静态属性 ,最后是子类构造函数

注意
(1)子类在继承父类后,若重写了父类的方法,那么父类中这个方法会被隐藏,但是可以通过在构造方法中用super显示调用。
(2)父类的静态方法是不能被继承的
(3)如果父类没有写出默认的无参构造方法但有写出有参的构造方法,那么在子类的构造方法中不需显示调用,不然会报错。
(4)静态代码块和静态变量只会在class加载时初始化,之后new新对象时如果没有显示的调用,不会再执行

  1. package com.zqt.basicproject;
  2. public class InitSort {
  3. public static void main(String[] args) {
  4. new Leaf();
  5. new Leaf();
  6. }
  7. }
  8. class Root {
  9. static int a = 1;
  10. static {
  11. System.out.println("Root的静态初始化块");
  12. System.out.println("Root的静态属性a:" + a);
  13. }
  14. {
  15. System.out.println("Root的普通初始化块");
  16. }
  17. public static void init() {
  18. System.out.println("Root的静态方法");
  19. }
  20. public void normal() {
  21. System.out.println("Root的非静态方法");
  22. }
  23. public Root() {
  24. a = 5;
  25. System.out.println("Root的无参构造");
  26. System.out.println("Root的无参构造后a:"+a);
  27. init();
  28. normal();//this.normal();结果:Mid的非静态方法
  29. }
  30. }
  31. class Mid extends Root {
  32. static int b = 2;
  33. int c = 3;
  34. static {
  35. System.out.println("Mid的静态初始化块");
  36. System.out.println("Mid的静态属性b:" + b);
  37. }
  38. {
  39. System.out.println("Mid的普通初始化块");
  40. System.out.println("Mid的非静态属性c:"+ c);
  41. }
  42. @Override
  43. public void normal() {
  44. //super.normal();
  45. System.out.println("Mid的非静态方法");
  46. }
  47. public Mid() {
  48. System.out.println("Mid的无参构造");
  49. }
  50. public Mid(String name) {
  51. this();
  52. super.init();
  53. super.normal();
  54. System.out.println("Mid的带参构造器" + name);
  55. }
  56. }
  57. class Leaf extends Mid {
  58. static {
  59. System.out.println("Leaf的静态初始化块");
  60. }
  61. {
  62. System.out.println("Leaf的普通初始化块");
  63. }
  64. public Leaf() {
  65. super("疯狂java讲义");
  66. System.out.println("Leaf的无参构造");
  67. }
  68. }

结果

  1. Root的静态初始化块
  2. Root的静态属性a1
  3. Mid的静态初始化块
  4. Mid的静态属性b2
  5. Leaf的静态初始化块
  6. Root的普通初始化块
  7. Root的无参构造
  8. Root的无参构造后a5
  9. Root的静态方法
  10. Mid的非静态方法
  11. Mid的普通初始化块
  12. Mid的非静态属性c:3
  13. Mid的无参构造
  14. Root的静态方法
  15. Root的非静态方法
  16. Mid的带参构造器疯狂java讲义
  17. Leaf的普通初始化块
  18. Leaf的无参构造
  19. Root的普通初始化块
  20. Root的无参构造
  21. Root的无参构造后a5
  22. Root的静态方法
  23. Mid的非静态方法
  24. Mid的普通初始化块
  25. Mid的非静态属性c:3
  26. Mid的无参构造
  27. Root的静态方法
  28. Root的非静态方法
  29. Mid的带参构造器疯狂java讲义
  30. Leaf的普通初始化块
  31. Leaf的无参构造

————————————————
版权声明:本文为CSDN博主「Damon_zqt」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Damon_zqt/article/details/105384791

java多态

1.当重写了父类的方法时。如果调用父类的方法,编译器会自动调用对应对象(子类对象)的方法实现。

  1. // polymorphism/PolyConstructors.java
  2. // Constructors and polymorphism
  3. // don't produce what you might expect
  4. class Glyph {
  5. void draw() {
  6. System.out.println("Glyph.draw()");
  7. }
  8. Glyph() {
  9. System.out.println("Glyph() before draw()");
  10. draw();
  11. System.out.println("Glyph() after draw()");
  12. }
  13. }
  14. class RoundGlyph extends Glyph {
  15. private int radius = 1;
  16. RoundGlyph(int r) {
  17. radius = r;
  18. System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
  19. }
  20. @Override
  21. void draw() {
  22. System.out.println("RoundGlyph.draw(), radius = " + radius);
  23. }
  24. }
  25. public class PolyConstructors {
  26. public static void main(String[] args) {
  27. new RoundGlyph(5);
  28. }
  29. }

输出:[

](https://blog.csdn.net/weixin_30233335/article/details/114390832)

  1. Glyph() before draw()
  2. RoundGlyph.draw(), radius = 0
  3. Glyph() after draw()
  4. RoundGlyph.RoundGlyph(), radius = 5

Glyph 的 draw() 被设计为可重写,在 RoundGlyph 这个方法被重写。但是 Glyph 的构造器里调用了这个方法,结果调用了 RoundGlyph 的 draw() 方法,这看起来正是我们的目的。输出结果表明,当 Glyph 构造器调用了 draw() 时,radius 的值不是默认初始值 1 而是 0。这可能会导致在屏幕上只画了一个点或干脆什么都不画,于是我们只能干瞪眼,试图找到程序不工作的原因。

前一小节描述的初始化顺序并不十分完整,而这正是解决谜团的关键所在。初始化的实际过程是:

  1. 在所有事发生前,分配给对象的存储空间会被初始化为二进制 0。
  2. 如前所述调用基类构造器。此时调用重写后的 draw() 方法(是的,在调用 RoundGraph 构造器之前调用),由步骤 1 可知,radius 的值为 0。
  3. 按声明顺序初始化成员。
  4. 最终调用派生类的构造器。

这么做有个优点:所有事物至少初始化为 0(或某些特殊数据类型与 0 等价的值),而不是仅仅留作垃圾。这包括了通过组合嵌入类中的对象引用,被赋予 null。如果忘记初始化该引用,就会在运行时出现异常。观察输出结果,就会发现所有事物都是 0。
另一方面,应该震惊于输出结果。逻辑方面我们已经做得非常完美,然而行为仍不可思议的错了,编译器也没有报错(C++ 在这种情况下会产生更加合理的行为)。像这样的 bug 很容易被忽略,需要花很长时间才能发现。

因此,编写构造器有一条良好规范:做尽量少的事让对象进入良好状态。如果有可能的话,尽量不要调用类中的任何方法。在构造器中唯一能安全调用的只有基类的 final 方法(包括 private 方法,它们自动属于 final)。这些方法不能被重写,因此不会产生意想不到的结果。你可能无法永远遵循这条规范,但应该朝着它努力。

2.子类中重写父类的方法时,可以返回父类方法返回类型的子类型

  1. // polymorphism/CovariantReturn.java
  2. class Grain {
  3. @Override
  4. public String toString() {
  5. return "Grain";
  6. }
  7. }
  8. class Wheat extends Grain {
  9. @Override
  10. public String toString() {
  11. return "Wheat";
  12. }
  13. }
  14. class Mill {
  15. Grain process() {
  16. return new Grain();
  17. }
  18. }
  19. class WheatMill extends Mill {
  20. @Override
  21. Wheat process() {
  22. return new Wheat();
  23. }
  24. }