Java入门系列04 -- 继承,注解,访问控制,static,单例模式,final,常量,嵌套类,内部类,局部类,抽象类

继承

  • 继承使用extends关键字

Object

  • 任何类最终都继承自java.lang.Object,一般称之为基类;

同名的成员变量

  • 子类可以定义与父类同名的成员变量,但不推荐这么做;

方法的重写

  • 子类的方法签名与父类的一样,叫做方法的覆盖,重写;
  • 子类重写的方法返回值类型必须 <= 父类的方法返回值类型;
  • 子类重写的方法权限 >= 父类的方法权限;

super

  • 访问父类的成员变量;
  • 调用父类中定义的方法,包括构造方法;

构造方法的细节

  • 子类的构造方法,必须先调用父类的构造方法,然后再执行后面的代码;
  • 案例代码:定义两个类Student与Person且Student继承自Student
public class Person {
    
    public Person () {
        System.out.println("Person()");
    }
}
public class Student extends Person{
    
    public Student () {
        System.out.println("Student()");
    }
}
public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Student student = new Student();
    }
}
  • 发现new Student(),依然会首先调用父类Person的构造方法;
  • 如果子类的构造方法没有显示调用父类的构造方法,编译器会自动调用父类无参的构造方法,若此时父类没有无参的构造方法,编译器将报错;
public class Person {
    
    public Person () {
        System.out.println("Person()");
    }
    
    public Person (int age) {
        
    }
}
  • new Student(),会首先调用父类Person的构造方法public Person (),Person现还有一个构造方法public Person (int age),若此时将
    public Person ()删除,程序将会报错,因为new Student(),会首先调用父类Person的构造方法,发现找不到目标方法;还可以改成Student构造方法,调用父类的构造方法;
public class Student extends Person{
    
    public Student () {
        super(0);
        System.out.println("Student()");
        
    }
}

注解Annotation

  • 两个常见的注解:
    • @override:告诉编译器这是一个重写后的方法;
    • @SuppressWarnings("警告类别"):让编译器不生成警告信息;
image.png
  • student变量没有被使用,会被警告,消除警告,可选中student,点击command + 1,出现如下所示:
    image.png
  • 最后成如下所示,消除了警告:
image.png

访问控制

  • Java中有4个级别的访问权限,从高到低如下所示:
    • public:在任何地方都是可见的;
    • protected:仅在自己的包中,自己的子类中可见;
    • 无修饰符(package-private):仅在自己的包中可见;
    • private:仅在自己的类中可见;
  • 使用注意点:
    • 上述4个访问权限都可以修饰类的成员,比如成员变量,方法,嵌套类等;
    • 只有public无修饰符(package-private)可以修饰顶级类(Top-level Class)
    • 上述4个权限不可以修饰局部类,局部变量;
    • 一个Java源文件中可以定义多个顶级类,public顶级类的名字必须和文件名一样;
image.png
  • 在Java中,可以类嵌套,即A类中可以定义一个B类,那么最外层的类A,就可被称之为顶级类;

封装

  • 在Java中封装:是指类的成员变量私有化,只能在本类中访问,提供setter,getter方法让外界进行访问,如下所示:
package com.lyy.model;

public class Person {
    private String name;
    private int age;
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
  • 生成getter,setter方法的快捷键 shift + command + S,或者
    编辑区右击 -> source -> 选择生成getter,setter方法

Static

  • static:常用来修饰类的成员,包括成员变量,方法,嵌套类;

  • 成员变量被static修饰时,

    • 称之为类变量,静态变量,其在程序运行过程中只占用一份固定的内存(存储在方法区),可达到数据共享;
    • 可以通过类名实例对象进行访问;
  • 成员变量没有被static修饰时,

    • 称之为实例变量,在每个实例对象内部都有一份;
    • 只能通过实例对象进行访问;
  • 方法被Static修饰时,称之为类方法,静态方法,通过类名进行调用;

    • 可以通过类名实例对象进行访问;
    • 内部不可以使用this关键字;
    • 可以直接访问类方法,类变量;
    • 不可以直接访问实例方法,实例变量;
    • 上述的规则都可以从this的本质进行解释;
  • 方法没有被static修饰时,称之为实例方法;

    • 只能通过实例对象进行访问,不可以通过类进行访问;
    • 内部可以使用this;
    • 可以直接访问实例变量,实例方法;
    • 可以直接访问类变量,类方法;
  • 虽然可以通过实例对象去访问类变量与类方法,但一般情况下不推荐这么使用,还是使用类名去访问类变量与类方法;

静态导入

  • 使用静态导入后,就可以省略类名来访问静态成员(成员变量,方法,嵌套类)
  • 案例代码:
package com.sf;
import static com.sf.Person.*;

public class Main {

    public static void main(String[] args) {
        
        Person.age = 20;
        System.out.println(Person.age);
        Person.run();
        
        //使用静态导入后 可省略类名直接访问
        age = 10;
        System.out.println(age);
        run();
    }
}
  • import static com.sf.Person.*; 属于静态导入;
  • 静态导入的经典使用场景:
package com.sf;
import static java.lang.Math.PI;

public class Main {
    public static void main(String[] args) {
        System.out.println(2 * PI * 10);
    }
}
  • 省略类名,可直接使用PI

成员变量的初始化

  • 编译器会自动为未初始化的成员变量设置初始值;
  • 手动给实例变量进行初始化:
    • 在声明中;
    • 在构造方法中;
    • 在初始化块中;
  • 在初始化块中,编译器会将初始化块复制到每个构造方法的头部,每创建一个实例对象,就会执行一次初始化块
  • 案例代码:
package com.sf;

public class Person {
    //声明
    public int age = 10;
    public String name = "yanzi";
    
    //构造方法
    public Person () {
        age = 10;
        name = "yanzi";
    }
    
    //初始化块
    {
        age = 100;
        name = "yanzi33";
    }
}
  • 手动给类变量进行初始化:
    • 在声明中;
    • 在静态初始化块中;
  • 当一个类被初始化时 执行静态初始化块,只会执行一次
  • 当一个类第一次被主动使用时,JVM会对类进行初始化;
  • 案例代码:
package com.sf;

public class Person {
    //声明
    public static int age = 10;
    
    //静态初始化块 
    static {
        age = 100;
    }
}
  • 可以有多个(静态)初始化块,按照在源码中出现的顺序被执行;
  • 案例代码:
package com.sf;

public class Person {
    //静态初始化块 
    static {
        System.out.println("Person -- static");
    }
    
    //初始化块
    {
        System.out.println("Person -- init");
    }
    
    //构造方法
    public Person() {
        System.out.println("Person -- 构造");
    }
}
package com.sf;

public class Student extends Person{
    //静态初始化块 
    static {
        System.out.println("Student -- static");
    }
        
    //初始化块
    {
        System.out.println("Student -- init");
    }
        
    //构造方法
    public Student() {
        System.out.println("Student -- 构造");
    }
}
package com.sf;

public class Main {

    public static void main(String[] args) {
        Student student = new Student();
    }
}
  • 执行顺序为:
image.png
  • 类的初始化,实例的初始化,构造方法的调用都是首先完成父类的,然后再执行子类的;

单例模式

  • 如果一个类设计成单例模式,那么在程序运行过程中,这个类只能创建一个实例对象;
  • 饿汉式单例模式:
/**
 * 饿汉式单例模式
 */
public class YYRocket {
    //私有的静态的 实例变量
    private  static  YYRocket instance = new YYRocket();

    //构造方法私有化
    private  YYRocket() {}

    //公共的静态方法,返回唯一的实例对象
    public  static  YYRocket getInstance() {
        return  instance;
    }
}
  • 懒汉式单例模式
public class YYRocket {
    //私有的静态的 实例变量
    private  static  YYRocket instance = null;

    //构造方法私有化
    private  YYRocket() {}

    //公共的静态方法,返回唯一的实例对象
    //存在线程安全问题
    public  static  YYRocket getInstance() {
        if (instance == null) {
            instance = new YYRocket();
        }
        return instance;
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        YYRocket rocket1 = YYRocket.getInstance();
        YYRocket rocket2 = YYRocket.getInstance();
        YYRocket rocket3 = YYRocket.getInstance();

        Log.v("YYRocket",String.valueOf(rocket1 == rocket2));
        Log.v("YYRocket",String.valueOf(rocket1 == rocket3));
        Log.v("YYRocket",String.valueOf(rocket2 == rocket3));
    }
}
  • 打印结果为true,推荐使用饿汉式单例模式,效率高;

final

  • final可以修饰类,被final修饰的类,不能被继承;
  • final可以修饰方法,被final修饰的方法,不能被子类重写;
  • final可以修饰变量,被final修饰的变量,只能进行一次赋值,可视作常量;

常量

  • 常量的写法:
public class YYPerson {
    public static final double HEIGHT = 172.5;
}
  • static final 一起修饰常量,常量通常大写;
  • 如果将基本类型或字符串定义为常量,并且在编译时就能确定值,那么编译器会使用常量值替代各处的常量名,类似于C语言的宏替换,通常称为编译时常量;
public class MainActivity extends AppCompatActivity {

    static final int AGE = 100;
    static final String name = "yanzi";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.v("MainActivity",String.valueOf(AGE));
        Log.v("MainActivity",String.valueOf(100));
        
        Log.v("MainActivity",name);
        Log.v("MainActivity","yanzi");
    }
}

嵌套类

  • 嵌套类:定义在另一个类中的类;
public class OuterClass {

    //非静态嵌套类 (内部类)
    class InnerClass {

    }

    //静态嵌套类
    static class StaticInnerClass {

    }
}
  • InnerClassy与StaticInnerClass都可称之为嵌套类;
  • 非静态嵌套类,可称之为内部类InnerClass;
  • 在嵌套类外层的类,称之为外部类OuterClass;
  • 最外层的外部类,称之为顶级类;

内部类

  • 没有被static修饰的嵌套类,即非静态嵌套类;
  • 内部类与实例变量,实例方法一样,其与外部类的实例对象相关联,也就是说要想使用内部类,首先必须外部类实例化对象,
  • 内部类不能定义除编译时常量以外的任何static成员;
  • 内部类可以直接访问外部类中所有成员,即使被声明为private;
  • 外部类可以直接访问内部类的成员,即使被声明为private;
  • 案例代码一:
package com.example.java_test.java;
import android.util.Log;

public class Person {

    private int age;

    public int getAge() {
        return age;
    }

    //非静态嵌套类 (内部类)
    public class Hand {
        private double height;
    }
}
import com.example.java_test.java.Person;
import com.example.java_test.java.Person.Hand;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Person person = new Person();
        //通过外部类实例 创建内部类实例对象
        Hand hand1 = person.new Hand();
        Hand hand2 = person.new Hand();
    }
}
  • 内存布局如下:
image.png
  • 案例代码一:
package com.example.java_test.java;
import android.util.Log;

public class Person {

    private int age;

    public int getAge() {
        return age;
    }
    //访问内部类private成员
    public void run(Hand hand) {
        Log.v("Hand",String.valueOf(hand.height));
    }

    //非静态嵌套类 (内部类)
    public class Hand {
        private double height;
        //编译时常量
        private static final int width = 10;
        //访问外部类private成员
        public void show() {
            Log.v("Hand",String.valueOf(age));
        }
    }
}
  • 内部类的细节:
import android.util.Log;

public class OuterClass {
    private int x = 1;
    public class InnterClass {
        private int x = 2;
        public void show() {
            Log.v("InnterClass",String.valueOf(x)); //2
            Log.v("InnterClass",String.valueOf(this.x)); //2
            Log.v("InnterClass",String.valueOf(OuterClass.this.x)); //1
        }
    }
}

静态嵌套类

  • 静态嵌套类:被static修饰的嵌套类;
  • 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中;
public class YYPerson {

    public static class YYCar {

    }
}
import com.example.java_test.java.YYPerson.YYCar;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        YYPerson person = new YYPerson();
        //两种创建方式等价
        YYPerson.YYCar car = new YYPerson.YYCar();
        YYCar car = new YYCar();
    }
}
  • YYPerson与YYCar两者之间没有必然联系,所以说静态嵌套类在行为上就是一个顶级类;
  • 对比一般的顶级类,静态嵌套类多了一些特殊的权限;
    • 可以直接访问外部类中成员,即使被声明为private;
  • 嵌套类的使用场景:
    • 如果类A只用在类C内部,可以考虑将类A嵌套到类C中;
      • 封装性更好;
      • 程序包更加简化;
      • 增强可读性,维护性;
    • 如果类A需要经常要访问类C的非公共成员,可以考虑将类A嵌套到类C中;
      • 另外也可以根据需要将类A隐藏起来,不对外暴露;
  • 如果需要经常访问非公共的实例成员,设计成内部类,否则设计成静态嵌套类;
    • 如果必须先有C实例,才能创建A实例,那么可以考虑将类A嵌套到类C中;

局部类

  • 局部类:定义在代码块中的类(可以定义在方法中,for循环中,if语句中等等);
  • 局部类不能定义除编译时常量以外的任何static成员;
  • 局部类只能访问final 或者 有效final的局部变量;
    • 从Java8开始,如果局部变量没有被第二次赋值,就可认定为有效final;
  • 局部类可以直接访问外部类中的所有成员,即使被声明为private;
    • 局部类只有定义在实例相关的代码块,才能直接访问外部类中的实例成员;
import android.util.Log;

public class YYPerson {

    private int width;

    //静态初始化块 与 类相关
    static {
        class A {

        }
    }

    //初始化块 与实例相关
    {
        class A {

        }
    }

    public void test() {
        //有效final
        int a = 10; //赋值会报错
        //final
        final int b = 11;

        for (int i = 0; i < 10; i++) {
            class A {

            }
        }

        if (true) {
            //局部类
            class A {
                void test() {
                    Log.v("test",String.valueOf(a));
                    Log.v("test",String.valueOf(b));

                    Log.v("test",String.valueOf(width));
                }
            }
        }
    }
}

抽象方法

  • 抽象方法:被abstract修饰的方法;
    • 只有方法声明,没有方法实现;
    • 不能时private权限(因为定义抽象方法的目的是让子类去实现)
    • 只能是实例方法,不能是类方法;
    • 只能定义在抽象类,接口中;

抽象类

  • 抽象类:被abstract修饰的类;
  • 定义抽象类的目的是给其他类继承的,所以不能用final修饰;
  • 可以定义抽象方法;
  • 不能实例化,但可以自定义构造方法;
  • 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类);
  • 可以像非抽象类一样定义成员变量,常量,嵌套类型,初始化块,非抽象方法等;
  • 使用场景:
    • 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的定义成抽象方法;
public abstract class Shap {

    protected double area;
    protected double girth;

    public double getArea() {
        return area;
    }

    public double getGirth() {
        return girth;
    }
    //抽象方法
    abstract public void show();
}
package com.example.java_test.java;

import android.util.Log;

public class Rectangle extends Shap{

    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void show() {
        area = width * height;
        girth = (width + height) * 2;
        Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
    }
}
import android.util.Log;

public class Circle extends Shap{

    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void show() {
        area = Math.PI * radius * radius;
        girth = Math.PI * radius * 2;
        Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
    }
}
import com.example.java_test.java.Circle;
import com.example.java_test.java.Rectangle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Circle circle = new Circle(10);
        circle.show();

        Rectangle rectangle = new Rectangle(10,20);
        rectangle.show();
    }
}

你可能感兴趣的:(Java入门系列04 -- 继承,注解,访问控制,static,单例模式,final,常量,嵌套类,内部类,局部类,抽象类)