static

1.静态变量

  • 静态变量:又称为类变量,类所有的实例都共享静态变量,也可以直接通过类名来访问静态变量,静态变量在内存中只存在一份。
  • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。

    注意:有static的: 用类名去调 (注意:静态不会空指针。) 没有static的: 先new对象用引用去调。

    • 存储位置
      • 静态变量存储在方法区中的静态区。
      • 实例变量存储在堆内存。
      • 局部变量储存在栈内存。
  1. public class A {
  2. private int x; // 实例变量
  3. private static int y; // 静态变量
  4. public static void main(String[] args) {
  5. // int x = A.x; // Non-static field 'x' cannot be referenced from a static context
  6. A a = new A();
  7. int x = a.x;
  8. int y = A.y;
  9. }
  10. }

2.静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。

  1. public abstract class A {
  2. public static void func1(){
  3. }
  4. // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
  5. }

只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。

  1. public class A {
  2. private static int x;
  3. private int y;
  4. public static void func1(){
  5. int a = x;
  6. // int b = y; // Non-static field 'y' cannot be referenced from a static context
  7. // int b = this.y; // 'A.this' cannot be referenced from a static context
  8. }
  9. }

3. 静态语句块
静态语句块在类初始化时运行一次。

  1. public class A {
  2. static {
  3. System.out.println("123");
  4. }
  5. public static void main(String[] args) {
  6. A a1 = new A();
  7. A a2 = new A();
  8. }
  9. }

4.静态内部类
非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。

  1. public class OuterClass {
  2. class InnerClass {
  3. }
  4. static class StaticInnerClass {
  5. }
  6. public static void main(String[] args) {
  7. // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
  8. OuterClass outerClass = new OuterClass();
  9. InnerClass innerClass = outerClass.new InnerClass();
  10. StaticInnerClass staticInnerClass = new StaticInnerClass();
  11. }
  12. }

静态内部类不能访问外部类的非静态的变量和方法。
5. 静态导包
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。

  1. import static com.xxx.ClassName.*

6. 初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。

  1. public static String staticField = "静态变量";
  1. static {
  2. System.out.println("静态语句块");
  3. }
  1. public String field = "实例变量";
  1. {
  2. System.out.println("普通语句块");
  3. }

最后才是构造函数的初始化。

  1. public InitialOrderTest() {
  2. System.out.println("构造函数");
  3. }

存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)

    继承

    1.案例

    2.static%26继承 - 图1

    1.1 案例代码实现

    1.父类Human类

    1. public class Human {
    2. // 合理隐藏
    3. private String name ;
    4. private int age ;
    5. // 合理暴露
    6. public String getName() {
    7. return name;
    8. }
    9. public void setName(String name) {
    10. this.name = name;
    11. }
    12. public int getAge() {
    13. return age;
    14. }
    15. public void setAge(int age) {
    16. this.age = age;
    17. }
    18. }

    2.子类Teacher类

  1. public class Teacher extends Human {
  2. // 工资
  3. private double salary ;
  4. // 特有方法
  5. public void teach(){
  6. System.out.println("老师在认真教技术!");
  7. }
  8. public double getSalary() {
  9. return salary;
  10. }
  11. public void setSalary(double salary) {
  12. this.salary = salary;
  13. }
  14. }

3.子类Student类

  1. public class Student extends Human{
  2. }

4.子类BanZhuren类

  1. public class Teacher extends Human {
  2. // 工资
  3. private double salary ;
  4. // 特有方法
  5. public void admin(){
  6. System.out.println("班主任强调纪律问题!");
  7. }
  8. public double getSalary() {
  9. return salary;
  10. }
  11. public void setSalary(double salary) {
  12. this.salary = salary;
  13. }
  14. }

5.测试类

  1. public class Test {
  2. public static void main(String[] args) {
  3. Teacher dlei = new Teacher();
  4. dlei.setName("播仔");
  5. dlei.setAge("31");
  6. dlei.setSalary(1000.99);
  7. System.out.println(dlei.getName());
  8. System.out.println(dlei.getAge());
  9. System.out.println(dlei.getSalary());
  10. dlei.teach();
  11. BanZhuRen linTao = new BanZhuRen();
  12. linTao.setName("灵涛");
  13. linTao.setAge("28");
  14. linTao.setSalary(1000.99);
  15. System.out.println(linTao.getName());
  16. System.out.println(linTao.getAge());
  17. System.out.println(linTao.getSalary());
  18. linTao.admin();
  19. Student xugan = new Student();
  20. xugan.setName("播仔");
  21. xugan.setAge("31");
  22. //xugan.setSalary(1000.99); // xugan没有薪水属性,报错!
  23. System.out.println(xugan.getName());
  24. System.out.println(xugan.getAge());
  25. }
  26. }

1.2小结

1.继承实际上是子类相同的属性和行为可以定义在父类中,子类特有的属性和行为由自己定义,这样就实现了相同属性和行为的重复利用,从而提高了代码复用。
2.子类继承父类,就可以直接得到父类的成员变量和方法。是否可以继承所有成分呢?请看下节!

2.子类不能继承的内容

子类不能继承父类的构造方法。
值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。

  1. public class Demo03 {
  2. public static void main(String[] args) {
  3. Zi z = new Zi();
  4. System.out.println(z.num1);
  5. // System.out.println(z.num2); // 私有的子类无法使用
  6. // 通过getter/setter方法访问父类的private成员变量
  7. System.out.println(z.getNum2());
  8. z.show1();
  9. // z.show2(); // 私有的子类无法使用
  10. }
  11. }
  12. class Fu {
  13. public int num1 = 10;
  14. private int num2 = 20;
  15. public void show1() {
  16. System.out.println("show1");
  17. }
  18. private void show2() {
  19. System.out.println("show2");
  20. }
  21. public int getNum2() {
  22. return num2;
  23. }
  24. public void setNum2(int num2) {
  25. this.num2 = num2;
  26. }
  27. }
  28. class Zi extends Fu {
  29. }

3.继承后的特点—成员变量

成员变量不重名
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。代码如下:

  1. class Fu {
  2. // Fu中的成员变量
  3. int num = 5;
  4. }
  5. class Zi extends Fu {
  6. // Zi中的成员变量
  7. int num2 = 6;
  8. // Zi中的成员方法
  9. public void show() {
  10. // 访问父类中的num
  11. System.out.println("Fu num="+num); // 继承而来,所以直接访问。
  12. // 访问子类中的num2
  13. System.out.println("Zi num2="+num2);
  14. }
  15. }
  16. class Demo04 {
  17. public static void main(String[] args) {
  18. // 创建子类对象
  19. Zi z = new Zi();
  20. // 调用子类中的show方法
  21. z.show();
  22. }
  23. }
  24. 演示结果:
  25. Fu num = 5
  26. Zi num2 = 6

成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。代码如下:

  1. class Fu1 {
  2. // Fu中的成员变量。
  3. int num = 5;
  4. }
  5. class Zi1 extends Fu1 {
  6. // Zi中的成员变量
  7. int num = 6;
  8. public void show() {
  9. // 访问父类中的num
  10. System.out.println("Fu num=" + num);
  11. // 访问子类中的num
  12. System.out.println("Zi num=" + num);
  13. }
  14. }
  15. class Demo04 {
  16. public static void main(String[] args) {
  17. // 创建子类对象
  18. Zi1 z = new Zi1();
  19. // 调用子类中的show方法
  20. z1.show();
  21. }
  22. }
  23. 演示结果:
  24. Fu num = 6
  25. Zi num = 6

子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果此时想访问父类成员变量如何解决呢?我们可以使用super关键字。
super访问父类成员变量
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super 关键字,修饰父类成员变量,类似于之前学过的 this 。
需要注意的是:super代表的是父类对象的引用,this代表的是当前对象的引用。
使用格式:
super.父类成员变量名

  1. class Fu {
  2. // Fu中的成员变量。
  3. int num = 5;
  4. }
  5. class Zi extends Fu {
  6. // Zi中的成员变量
  7. int num = 6;
  8. public void show() {
  9. int num = 1;
  10. // 访问方法中的num
  11. System.out.println("method num=" + num);
  12. // 访问子类中的num
  13. System.out.println("Zi num=" + this.num);
  14. // 访问父类中的num
  15. System.out.println("Fu num=" + super.num);
  16. }
  17. }
  18. class Demo04 {
  19. public static void main(String[] args) {
  20. // 创建子类对象
  21. Zi1 z = new Zi1();
  22. // 调用子类中的show方法
  23. z1.show();
  24. }
  25. }
  26. 演示结果:
  27. method num=1
  28. Zi num=6
  29. Fu num=5

小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。

4.继承后的特点—成员方法

成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

  1. class Fu {
  2. public void show() {
  3. System.out.println("Fu类中的show方法执行");
  4. }
  5. }
  6. class Zi extends Fu {
  7. public void show2() {
  8. System.out.println("Zi类中的show2方法执行");
  9. }
  10. }
  11. public class Demo05 {
  12. public static void main(String[] args) {
  13. Zi z = new Zi();
  14. //子类中没有show方法,但是可以找到父类方法去执行
  15. z.show();
  16. z.show2();
  17. }
  18. }

成员方法重名
如果子类父类中出现重名的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。

  1. class Fu {
  2. public void show() {
  3. System.out.println("Fu show");
  4. }
  5. }
  6. class Zi extends Fu {
  7. //子类重写了父类的show方法
  8. public void show() {
  9. System.out.println("Zi show");
  10. }
  11. }
  12. public class ExtendsDemo05{
  13. public static void main(String[] args) {
  14. Zi z = new Zi();
  15. // 子类中有show方法,只执行重写后的show方法
  16. z.show(); // Zi show
  17. }
  18. }

5.方法重写

存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
为了满足里式替换原则,重写有以下三个限制:

  • 子类方法的访问权限必须大于等于父类方法;
  • 子类方法的返回类型必须是父类方法返回类型或为其子类型。
  • 子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。

使用 @Override 注解,可以让编译器帮忙检查是否满足上面的三个限制条件。
下面的示例中,SubClass 为 SuperClass 的子类,SubClass 重写了 SuperClass 的 func() 方法。其中:

  • 子类方法访问权限为 public,大于父类的 protected。
  • 子类的返回类型为 ArrayList,是父类返回类型 List 的子类。
  • 子类抛出的异常类型为 Exception,是父类抛出异常 Throwable 的子类。
  • 子类重写方法使用 @Override 注解,从而让编译器自动检查是否满足限制条件。 ```java class SuperClass { protected List func() throws Throwable {
    1. return new ArrayList<>();
    } }

class SubClass extends SuperClass { @Override public ArrayList func() throws Exception { return new ArrayList<>(); } }

  1. 在调用一个方法时,先从本类中查找看是否有对应的方法,如果没有再到父类中查看,看是否从父类继承来。否则就要对参数进行转型,转成父类之后看是否有对应的方法。总的来说,方法调用的优先级为:
  2. - this.func(this)
  3. - super.func(this)
  4. - this.func(super)
  5. - super.func(super)
  6. ```java
  7. /*
  8. A
  9. |
  10. B
  11. |
  12. C
  13. |
  14. D
  15. */
  16. class A {
  17. public void show(A obj) {
  18. System.out.println("A.show(A)");
  19. }
  20. public void show(C obj) {
  21. System.out.println("A.show(C)");
  22. }
  23. }
  24. class B extends A {
  25. @Override
  26. public void show(A obj) {
  27. System.out.println("B.show(A)");
  28. }
  29. }
  30. class C extends B {
  31. }
  32. class D extends C {
  33. }
  1. public static void main(String[] args) {
  2. A a = new A();
  3. B b = new B();
  4. C c = new C();
  5. D d = new D();
  6. // 在 A 中存在 show(A obj),直接调用
  7. a.show(a); // A.show(A)
  8. // 在 A 中不存在 show(B obj),将 B 转型成其父类 A
  9. a.show(b); // A.show(A)
  10. // 在 B 中存在从 A 继承来的 show(C obj),直接调用
  11. b.show(c); // A.show(C)
  12. // 在 B 中不存在 show(D obj),但是存在从 A 继承来的 show(C obj),将 D 转型成其父类 C
  13. b.show(d); // A.show(C)
  14. // 引用的还是 B 对象,所以 ba 和 b 的调用结果一样
  15. A ba = new B();
  16. ba.show(c); // A.show(C)
  17. ba.show(d); // A.show(C)
  18. }

6.继承后的特点—构造方法

构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子
继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

  1. class Person {
  2. private String name;
  3. private int age;
  4. public Person() {
  5. System.out.println("父类无参");
  6. }
  7. // getter/setter省略
  8. }
  9. class Student extends Person {
  10. private double score;
  11. public Student() {
  12. //super(); // 调用父类无参,默认就存在,可以不写,必须在第一行
  13. System.out.println("子类无参");
  14. }
  15. public Student(double score) {
  16. //super(); // 调用父类无参,默认就存在,可以不写,必须在第一行
  17. this.score = score;
  18. System.out.println("子类有参");
  19. }
  20. }
  21. public class Demo07 {
  22. public static void main(String[] args) {
  23. Student s1 = new Student();
  24. System.out.println("----------");
  25. Student s2 = new Student(99.9);
  26. }
  27. }
  28. 输出结果:
  29. 父类无参
  30. 子类无参
  31. ----------
  32. 父类无参
  33. 子类有参

7.super

1.super可以用来引用直接父类的实际变量(和this相似,主要用来区分父类和子类的字段)
2.super可以用来调用直接父类构造函数(super()一定要放在第一行)
3.super可以用来调用直接父类方法.

  1. public class Main{
  2. public static void mian(String[] args)
  3. Child child = new Child("Father","Child");
  4. child.test();
  5. }
  6. }
  7. class Father{
  8. protected String name;
  9. public Father(String name){
  10. this.name=name;
  11. }
  12. public void Say(){
  13. System.out.println("hello,child");
  14. }
  15. }
  16. class Child extends Father{
  17. private Stirng mame;
  18. public Child(String name1,String name2){
  19. super(name1);//调用直接父类构造函数
  20. this.name=name2;
  21. }
  22. public void test(){
  23. System.out.println(this.name);
  24. System.out.println(super.name);//引用直接父类的实例变量
  25. super.Say(); //调用父类的直接方法
  26. }
  27. }
  • 访问父类的构造函数:

子类只能继承父类的默认构造函数,如果父类没有默认的构造函数,那子类不能从父类继承默认构造函数,
这时子类必须使用super()来实现对父类的非默认构造函数的调用.

  • 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。

this和super的区别

  • 相同点:
  1. super()和this()都必须在构造函数的第一行进行调用, 否则就是错误的
    2. this() 和super()都指的是对象,所以,均不可以在static环境中使用。
  • 不同点:
  1. super() 主要是对父类构造函数的调用,this() 是对重载构造函数的调用
    2. super() 主要是在继承了父类的子类的构造函数中使用,是在不同类中的使用; this() 主要
    是在同一类的不同构造函数中的使用

    8.继承的特点

    1.Java只支持单继承,不支持多继承。
    1. // 一个类只能有一个父类,不可以有多个父类。
    2. class A {}
    3. class B {}
    4. class C1 extends A {} // ok
    5. // class C2 extends A, B {} // error
    2.一个类可以有多个子类。
    1. // A可以有多个子类
    2. class A {}
    3. class C1 extends A {}
    4. class C2 extends A {}
    3.可以多层继承。
    1. class A {}
    2. class C1 extends A {}
    3. class D extends C1 {}

    顶层父类是Object类。所有的类默认继承Object,作为父类。