第7章 面向对象(下)

7.1 静态的

1、static:静态的

2、什么是静态的?

和对象无关的,不会因为对象的不同而不同,即所有对象都一样的。

换句话说,和对象无关。


动态的,根据对象的不同而不同,和对象有关,由对象动态决定。


3、static这个关键字用在哪里?

(1)成员变量前面:静态变量

(2)成员方法前面:静态方法

(3)代码块前面:静态代码块

(4)内部类class前面(后面和内部类一起讲):静态内部类

(5)在import语句里面:静态导入

7.1.1 静态变量

4、静态变量

(1)静态变量的定义:是指有static修饰的成员变量

(2)静态变量的特点:

A:静态变量的值是所有对象共享的。

B:虽然可以,但不建议通过“对象.静态变量”的形式进行访问,

建议通过“类名.静态变量”的形式进行访问。

C:如果静态变量的可见性范围被限制了,提供get/set时,它的get/set也是静态的。

在set方法中,静态变量和局部变量(形参)重名了,使用“类名.静态变量”进行区分。

D:静态变量的值是存储在“方法区”中。

//Company公司
public class Employee {
    public static String company;//静态变量
    public String name;//实例变量
    private static String corporation;

    public static String getCorporation() {
        return corporation;
    }

    public static void setCorporation(String corporation) {
        Employee.corporation = corporation;
        //不能使用this.
    }
}
public class TestStaticVariable {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();
        e1.company = "执影";

        System.out.println(e1.company);
        System.out.println(e2.company);
//        System.out.println(Employee.company);

        e2.company = "zhiying";

        System.out.println(e1.company);
        System.out.println(e2.company);

        System.out.println("-----------------");
        //静态变量推荐使用“类名.静态变量”的形式进行访问。
        Employee.company = "执影人";
        System.out.println(Employee.company);
    }
}

7.1.2 静态方法

5、静态方法

(1)静态方法的定义:是指有static修饰的方法。

(2)静态方法的特点:

A:静态方法中不允许直接使用本类的非静态成员

B:静态方法中也不允许直接使用this,super等关键字

C:静态方法的调用虽然可以用“对象.静态方法”的形式进行访问,

但是我们更推荐“类名.静态方法”的形式进行访问。

D:父类的静态方法可以被子类访问,但是不能被子类重写

public class Student {
    private static String school;
    private String name;
    public static String getSchool(){
        return school;
    }
    
    public static void method(){
        System.out.println(this.name);//错误,静态方法中不允许出现this
        System.out.println(name);//错误,静态方法中不允许直接访问非静态的实例变量name
        System.out.println(super.hashCode());//错误,静态方法也不允许出现super
    }
}
public class TestStudent {
    public static void main(String[] args) {
        System.out.println(Math.random());

        Student stu = new Student();
        System.out.println(stu.getSchool());
        //编译器也会处理成Student.getSchool()
        System.out.println(Student.getSchool());
    }
}

Java学习笔记(十)_System

public class Father {
    public static void method(){
        System.out.println("Father.method");
    }
}
/*
@Override:是一个注解。
作用:和编译器说,它标记的方法是子类重写父类的方法。请“严格”按照重写的要求进行格式检查。
如果违反重写的要求,就编译报错。

如果一个方法遵守重写的要求,加或不加@Override都可以。
如果一个方法没有遵守重写的要求,不加@Override有可能编译器不提示你错误,导致和你的意图不一致。

 建议:凡是重写的方法,不管你写的对不对,都加@Override。

 */
public class Son extends Father {

    //自己以为我重写成功了,但是其实是不对的
   /* public static void method(){
        System.out.println("Son.method");
    }*/
}
public class TestSon {
    public static void main(String[] args) {
        Father f = new Son();
        f.method();//完全等价于Father.method

        Father.method();
        Son.method();
    }
}

Java学习笔记(十)_System_02

7.1.3 静态代码块

6、静态代码块

(1)静态代码块的作用:为类的静态变量初始化

(2)静态代码块的执行特点

A:一个类的静态代码块只会执行一次

B:它是在类加载类初始化时执行,先于类对象的创建

(3)语法结构

【修饰符】 class 类名{

   static{

       静态代码块

   }

}

public class Demo {
    static {
        System.out.println("静态代码块");
    }

    public Demo(){
        System.out.println("Demo无参构造");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Demo d1 = new Demo();
        Demo d2 = new Demo();
    }
}

7.1.4 类初始化

7、类初始化

(1)类初始化:为类的静态变量赋值的过程

(2)类初始化相关代码

A:静态变量声明时,直接=值

B:静态代码块


(3)如果我们编写了类初始化相关代码,那么编译器会给我们

把这些代码按照编写顺序组装到一个<clinit>()的类初始化方法中。

cl:class

init:initialize


(4)一定是先完成类初始化,再进行实例初始化(创建对象),

并且一定是先完成父类初始化,再完成子类初始化。

public class SubDemo extends Demo {
    static {
        System.out.println("子类SubDemo的静态代码块");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        /*Demo d1 = new Demo();
        Demo d2 = new Demo();
*/
        SubDemo s1 = new SubDemo();
        SubDemo s2 = new SubDemo();
    }
}

7.1.5 静态导入

8、静态导入

(1)静态导入的定义:在import语句里面出现static,就是静态导入

(2)作用:在当前的.java文件中,使用另一个类的静态成员时,可以直接在代码中使用静态成员名,而不用“类名.静态成员名。

import static java.lang.Math.*;

public class TestStaticImport {
    public static void main(String[] args) {
        /*System.out.println(Math.PI);
        System.out.println(Math.random());
        System.out.println(Math.sqrt(9));*/

        System.out.println(PI);
        System.out.println(random());
        System.out.println(sqrt(9));
    }
}

7.2 抽象类和抽象方法

1、什么是抽象类?

在class前面加abstract修饰的都是抽象类。


【其他修饰符】 abstract class 类名{


}


2、为什么要用抽象类?

(1)不希望你创建这个类的对象,希望你创建它子类的对象(少一点)

(2)当我们在声明某个父类时,其中某些方法不得不声明,但是又无法给出具体的实现,

即无法编写方法体代码,这个方法体代码通常由子类来实现,各个子类的实现不同,

这个时候需要把这样的方法声明为抽象方法,

Java中包含抽象方法的类必须是抽象类(更多的情况)。


3、抽象类的特点

(1)抽象类不能直接new对象,只能new它子类的对象

(2)抽象类中可以包含抽象方法,也可以不包含抽象方法。

但是反过来,包含抽象方法的类,必须是抽象类。

(3)抽象类是用来被继承的,子类继承抽象类时,必须重写抽象类的所有抽象方法,

否则子类也得是抽象类。


4、抽象类和非抽象类有什么区别?

抽象类不能直接new对象,非抽象类可以直接new对象,

抽象类中可以包含抽象方法,非抽象类中不可以包含抽象方法。


5、抽象类中有构造器吗?

一定有

它的构造器存在的意义是被子类调用的。

public abstract class Person {
    private String name;
    private int age;

    public Person(){
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Student extends Person {
    //....
}
public class Teacher extends Person {
    //.....
}
public class TestPerson {
    public static void main(String[] args) {
        //做学生信息管理系统
        //在整个系统中,要么存在的是学生对象,要么存在的是教师对象
        //希望整个程序中不能直接new Person的对象。
        Person[] arr = new Person[3];
//        arr[0] = new Person();//报错,因为抽象类不允许直接new对象
        arr[1] = new Student();
        arr[2] = new Teacher();
    }
}
//包含抽象方法的类必须是抽象类
public abstract class Graphic {
    /*public double area(){
        return 0.0;
    }*/

    //抽象方法,没有方法体,有abstract修饰
    public abstract double area();
}
public  class Circle extends Graphic {
    private double radius;

    public Circle() {
    }

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

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                '}';
    }

	//不重写报错
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}
public class Rectangle extends Graphic{
    private double length;
    private double width;
    public Rectangle(){
    }

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

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                '}';
    }
    //不重写报错
	@Override
    public double area(){
        return length * width;
    }
}
public class TestGraphic {
    public static void main(String[] args) {
        Graphic[] arr = new Graphic[2];
        arr[0] = new Circle(2.5);
        arr[1] = new Rectangle(5,3);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i].area());
            /*
            arr[i]的编译时是Graphic类型,先要去Graphic类中匹配area()方法,
            此时Graphic类中没有area()方法,编译报错。
            为了编译通过,必须在Graphic类中,增加area()方法
             */
        }
    }
}

7.3 注解

7.3.1 基本注解

1、什么是注解?

在代码中,使用@开头的都是注解。


2、注解有什么用呢?

注解也是一种注释,这种注释是代码级别的注释,

它是让另一段代码来读取这个注解,并做出相应的xx操作。


例如:@Override注解。

当编译器(编译器也是一段程序)读取到某个方法上面加了@Override,

就会执行一段代码,这段代码是一段校验该方法是否满足重写要求的判断代码,

如果满足,那么编译通过,

如果不满足,编译器会报错,提醒你违反了重写的要求。


3、3个最最基本的系统注解(JDK1.8之前就有的)

(1)@Override注解

它只能加在重写的方法上面。

(2)@Deprecated注解

它可以用在方法、类、构造器等等上面。

作用:让编译器提醒程序员,如果你使用了这个方法、类、构造器等,编译器会弹出警告,

提醒你这个方法、类、构造器等已经过时了,可能存在设计的缺陷,或者其他问题,

不再推荐你继续使用。


为什么已过时的代码不修复或删掉呢?

修复:修复的成本太高,重新设计一个

   开发原则:面向修改关闭,面向扩展开发

删掉:因为这些方法、类、构造器等在旧的项目中还在使用。


   比喻:

       注销银行卡。

       不会删除你之前的所有的银行记录啥,

       而是标记为这个卡不能用。

       这个操作称为逻辑删除,不是物理删除。


(3)@SuppressWarnings

抑制警告

import java.util.Date;

public class TestDeprecated {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        Date today = new Date(2022-1900,8-1,20);
        //使用这个构造器时Date(int year, int month, int date)
        //编译器会弹出警告,说,这个方法已过时
        System.out.println(today);
        //Wed Sep 20 00:00:00 CST 3922
    }
}

7.3.2 文档注释

1、Java中有3种注释

(1)单行注释 //

(2)多行注释 /*    */

(3)文档注释/**    */


文档注释有严格的格式要求。

/**
 * 文档注释演示类
 * @author Irene
 * @version JDK1.8
 * @see java.lang.Object
 */
public class TestDoc {
    /**
     * 这是一个主方法,Java程序的入口
     * @param args String[] 它是命令行参数,可以通过java命令传入参数值。格式:java 主类名 参数值1 参数值2 ...
     */
    public static void main(String[] args) {
        //...
    }

    /**
     * 这是一个求两个整数最大值的方法
     * @param a int 第一个整数
     * @param b int 第二个整数
     * @return int 当a大于b时,返回a的值,否则返回b的值
     */
    public static int max(int a, int b){
        return a > b ? a : b;
    }

    /**
     * 这是求两个整数的商。
     * @param a int 被除数
     * @param b int 除数
     * @return int 商,a/b的结果,只有整数部分
     * @exception ArithmeticException 当b为0时可能发生算术异常
     */
    public static int divide(int a, int b){
        return a/b;
    }
}

Java学习笔记(十)_抽象类_03

7.3.3 JUnit

说明:标准的单元测试编写方式,在实际项目中再讲。

今天,讲单元测试的JUnit的小工具只是把它当成一个代替main方法运行的小工具而已。


使用步骤:

(1)在当前模块中要引入JUnit的库

因为JUnit不是JRE核心类库的一部分,它是第三方的一个工具。

引入的方式很简单:

第一步:先写一个方法

第二步:在方法上面加一个@Test

第三步:@Test会报错,鼠标放到@Test上面,IDEA会提示我们需要引入JUnit的库(一堆class文件)

第四步:单击Add 'JUnit4' to classpath

第五步:下载,默认是下载到C盘用户目录下的.m2的仓库中

   (至于后期项目中如何依赖和下载,咱们学习Maven时再学习)


(2)凡是要独立运行的测试方法上面都可以加@Test


使用要求:

(1)自己写的类名,同一个包的所有类,包括当前类,不能命名为Test类。

(2)要使用@Test标记测试方法,

这个测试方法必须是public,void,(),非静态的

(3)@Test标记的测试方法所在类必须只有唯一的无参构造,并且也是public的。

import org.junit.Test;

public class TestJUnit {
    @Test
    public void test01(){
        System.out.println("hello");
    }

    @Test
    public void test02(){
        System.out.println("world");
    }
}

Java学习笔记(十)_静态变量_04