面向对象编程、类和对象、封装、继承、多态、包、finally、static关键字、abstract抽象类、代码块全面总结

本文详细介绍了面向对象编程的核心概念,包括类和对象的定义,属性和方法的封装,以及private和public访问修饰符的使用。讨论了构造方法的作用和this关键字的含义,并探讨了继承的概念,super关键字的应用,以及多态的实现。此外,还讲解了包的组织作用,静态关键字static的用法,以及finally关键字在异常处理中的角色。最后,阐述了抽象类和抽象方法在设计上的意义及其在多态中的作用。
摘要由CSDN通过智能技术生成

面向对象编程

1.类和对象

Java是纯面向对象的编程语言,任何代码都需要写到类里面。

类:具有相同特征(属性)和行为(方法)的事物的抽象。类是抽象的,类是一个概念。在java中,类本质上是一个类型(引用类型)。

  • java提供了一种语法,允许你创建自己的类。

  • 类就是对事物的定义 类的作用是 作为一种数据类型。 类用于创建对象。

  • 一个java文件可以有多个类,但只能有一个public类,而且文件名必须和public修饰类的文件名相同。

  • 之所以只能有一个public类,是因为一个文件只能有一个文件名。

  • 一般情况下,一个文件只有一个类。

对象:对象是类的具体体现,是类的实例。它是具体的

OOP–面向对象编程。

POP–面向过程编程。

属性还可以叫成员变量,也可以叫实例变量。 类名 大驼峰法命名

属性有默认值:

整数(byte,short,int,long) 默认值是0

小数(float, double) 默认值是0.0

布尔类型(boolean) 默认值是false

char默认值值是 ‘0’

引用类型(例如,数组,String,等)默认值是null

实例变量和局部变量:

实例变量(属性): 在类中方法外的变量

局部变量:在方法中定义的变量(包括方法的形参)

区别实例变量(成员变量)局部变量
类中定义的位置不同类方法中方法内(含形参)
内存中的位置不同堆内存在栈区
生命周期不同随着对象的存在而存在,随着对象的消失而消失随着方法的调用而存在,随着方法的调用完毕而消失
初始值不同有默认的初始值没有默认的初始值,必须先定义,定义时要给初始值
作用域1. 类内;2. 对象使用时,随着对象使用只能在方法内使用,出了方法不能使用。
public class Phone {

    String brand;   //品牌
    String color;   //颜色
    String price;   //价格

    public void call(){
        System.out.println("打电话");
    }
    public void sentMsg(){
        System.out.println("发信息");
    }

}
public class TestPhone {
    public static void main(String[] args) {
        Phone phone = new Phone();  //1. 在栈区开辟一个地址, 2. 在堆区开辟一个地址 3. 给堆区赋初始值, 4.
        phone.brand = "IPhone 13 Pro Max";
        phone.color = "深空灰";
        phone.price = "8999";
        System.out.println(phone.brand);

        phone.call();
    }
}

2.封装

1. private

当在一段代码块中定义一个变量时,java就在中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。

如果一个方法 只是类内部使用,不想再外界使用。这样的方法可以定义成私有方法。

  • 一般来说 某一个功能的中间计算过程不需要对外公开,所以说可以定义成私有方法。

如果属性没有指定访问修饰符。那么属性默认的可见度是包级。

可见度一共有4种、修饰符 共3个 public protected private 缺省。可见度决定了对象是否可以使用点语法 访问属性或者方法。

一旦属性或者方法定义成 private 那么 属性或者方法只能在类的内部 使用。外部是不可以通过 对象.属性 或者 对象.方法 进行访问的

在实际开发过程中, 所有的属性都会定义成 private

private 相当于 禁用了 点语法

setter,getter 封装了属性的访问 功能性的方法(分数的求和,乘积等)封装了功能细节。

类封装了属性和方法

setter/getter和直接访问的异同

相同点:都是在访问属性,包括赋值和取值。

不同点:1.直接访问属性优势是简单快捷,一步到位;劣势是有可能会出现数据错误。

​ 2.通过setter/getter访问属性优势是数据没有直接赋值给属性,在赋值之前可以做一些操作和处理;劣势是代

码量略大。

例子:

public class Person {
    String name;
    Integer age;
    public Person(){}
    public Person(String name,Integer age){
        this.name = name;
        this.setAge(age);
    }

    public void eat(String food){
        System.out.println("吃饭" + food);
    }
    public void sleep(){
        System.out.println("睡觉");
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {   //对年龄的输入进行限制
        if (age <= 0){
            age =18;
        }
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

//在实际中,年龄不可能是负数,所以我们可以在调用setAge方法时加入条件判断,改进代码

实际开发中。定义一个类的时候。 属性定义成private, 提供getter/setter方法, 提供无参构造方法和有参构造方法,提供toString方法,提供功能性方法

访问修饰符 我们只会用到 private和public。属性用private修饰,方法用public修饰(不想公开的方法用private修饰)。

权限访问修饰符(是否有权限,使用.语法):也叫可见度访问修饰符

  • 可见度修饰符 可以修饰类,也可以修饰类的成员
  • 如果修饰类,只有两种可见度,public 缺省
  • 修饰类的成员 属性,方法 private 缺省 protected public
  • private 不能在类外部使用(同包的其他类)通过点语法访问
  • 如果跨包访问属性或者方法,只能访问public修饰符修饰的属性或者属性。 public

2. this关键字:

this的英文含义是这个,在代码中this是一个特殊的对象,它始终指的是调用方法的对象,即谁调用方

法,this就是谁。

  • this修饰的变量用于指代实例变量。方法的形参如果与实例变量同名,不带this的变量是形

参,而不是实例变量。方法的形参如果和实例变量不同名,不需要用this修饰。

  • this代表的就是调用方法的那个对象。

3. 构造方法

构造方法是一种特殊的方法,它是创建对象时调用的方法,用于在创建对象的时候对属性进行初始化。

特点:
1. 方法名和类名相同
2. 方法没有返回值。 而且不能写void
3. 构造方法不能独立使用,只能在创建对象的时候调用,即紧跟着new对象的时候使用。
4. 如果我们没有编写构造方法,系统将会为我们默认提供一个无参构造方法。 一旦我们提供了任何一个构造方法,系统将不再提供默认的无参构造。
5. 构造方法可以重载。方法重载:方法名相同,参数个数或者参数类型不同,这样的方法成为方法重载。
6. 构造方法的作用,给属性附上初始值。
7. 通常,我们会给类提供一个无参构造方法和一个有参构造方法。

4. 标准的Java类

标准Java类的定义原则:

  • 属性用private修饰
  • 提供属性对应的setter、getter方法
  • 提供1个或多个构造方法
  • 提供正常的功能性方法

5. 封装

封装:隐藏对象的内部细节,对外提供接口(访问方式)。

**封装的原则:**将类的某些信息隐藏在类内部,不允许外界直接访问,而是给外界提供接口(方法)。外

界通过接口访问内部的数据以及类的功能。

  1. getter、setter封装了实例变量。

  2. 方法封装了功能的实现细节。

  3. 类封装了属性和方法。

封装的好处:

  1. 通过方法来控制实例变量的操作,提高了代码的安全性。

  2. 把代码用方法进行封装,提高了代码的复用性。

3. 继承

面向对象的三大特性: 封装,继承,多态。

封装:不要对外暴露属性,属性定义成private,提供public修饰的setter/getter方法。 方法封装了实现细节。类封装了属性和方法。 在使用的时候,使用对象。

继承: 功能一:在保证程序功能不变的情况下,简化代码。

​ 功能二:可以站在巨人的肩膀上,扩展功能。

1. 子类、父类的构造方法

在Java中, 子类可以继承父类所有的属性和方法(构造方法除外)。----私有方法和私有属性也能继承。虽然能继承,但是不能通过 点语法 访问,可以间接访问。

在继承关系里,子类如果不满意继承过来的方法实现,可以重写方法。

子类可以继承父类所有的属性和方法(构造方法除外):。
子类不能继承父类的构造方法,是因为构造方法在子类中没有返回类型,方法名也与子类的类名不相同

子类不但可以继承父类的方法和属性,还可以添加自己独有的属性和方法。

在同一个类中,如果有多个方法具有相同的方法名,但是参数类型或者参数个数 不同(或者都不同)。这样的方法称为方法重载。 方法重载允许定义同名方法,能够适当的解决命名危机问题。

2.super关键字

super是一个特殊的关键字,这个关键字用于访问父类中的方法和属性(super相当于父类类名)。

  1. 通过super访问父类构造方法。语法:super(参数列表); 主要作用:给继承过来的属性赋初始值。通过super调用父类构造方法时,代码必须写在第一行。

  2. 通过super访问父类中的其他方法(非私有)。语法:super.方法名(参数列表);

  3. 通过super访问父类中的属性(非私有)。语法:super.属性名;

this和super区别:

    1. this是一个对象。 this是方法调用者。 this都是出现在方法中的, 谁调用方法,this就是谁。所以this可以访问方法,访问属性。
    1. super仅仅是一个关键字,不是对象。它可以调用父类中的属性(非私有),方法(非私有)以及构造方法

继承中构造方法的访问特点:如果子类构造方法中没有明确写出调用哪个父类构造方法,会默认调用父类的无参构造方法,即super();
即使你没有写super(),系统也会默认在第一行代码中调用super()。如果父类不提供无参构造方法,提供有参数的构造方法,子类会报错。

所有类的根类(祖先类) 是Object 如果一个类创建的时候,没有写父类,那么它的父类是Object

3. 重载和重写的区别:

重载在同一个类,重写在继承之间,子类重写父类的构造方法。

重写返回值类型,参数类型必须相同,重载返回类型,参数列表不同。

4. 多态

多态:指的是对象的多态性,同一对象在不同时刻表现出来的不同形态。

​ 父类引用 指向子类对象。接口 引用指向实现类对象。

多态的前提:

  1. 有继承或者实现关系

  2. 有方法重写

  3. 父类引用指向子类对象(或子类的对象赋值给父类的引用)

多态的特点:

  • 1.在继承关系里,子类可以被看做父类对象。   父类 引用 = new  子类();
    
  • 2.如果子类重写了父类中的方法,通过父类引用调用方法时,会执行子类的方法。
    
多态会屏蔽子类差异,只能调用父类中的方法(子类的公共方法)。

如果想要使用子类特有的方法, 需要把父类引用强转成子类。

代码示例:

public  class Animal {
    int a = 10;
    private String name;
    private Integer age;

    public Animal() {
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Animal(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public void eat(){
        System.out.println("动物吃东西");
    }
}

public class Cat extends Animal{
    
    public Cat() {
    }

    public Cat(String name, Integer age) {
        super(name, age);
    }

    @Override
    public void eat(){
        System.out.println("猫吃鱼");
    }

    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

public class Dot extends Animal{
    public Dot() {
    }

    public Dot(String name, Integer age) {
        super(name, age);
    }
    public void eat(){
        System.out.println("狗吃肉");
    }

    public void lookDoor(){
        System.out.println("狗看门");
    }
}


public class TestAnimal {
    public static void main(String[] args) {
        //父类引用指向子类对象
        Animal animal1 = new Cat("小猫",2);  //虽然子类当作父类来看,但是子类仍然使用自身的方法

        Cat cat = new Cat("小猫",2);
        
        System.out.println(cat);


        animal1.eat();   //猫吃🐟
        System.out.println(animal1.a);  //输出10;
		//System.out.println(animal1.b);   不能打印b  因为animal中没有b属性
        //为什么 调用方法的时候,是子类的方法, 但是属性确是父类的属性呢?
        //属性不是方法, 没有重写的概念。   所以点语法访问的是父类的属性  实际开发种,不会出现  父类子类具有同名属性的情况
		//animal1.catchMouse();   无法调用,因为animal1 的类型是Animal,所以编译器认为animal1是Animal类型,会检查Animal中是否有catchMouse方法
        System.out.println(animal1);

        Animal animal2 = new Dot("大狗",3);
        animal2.eat();
        System.out.println(animal2);
    }
}
成员编译期运行期
实例变量看等号左边(即父类)看等号左边(即父类)
实例方法看等号左边(即父类)看等号右边(即子类)

实例方法和实例变量的访问不一样,原因是实例方法有重写,实例变量没有重写

4.1 多态的应用——参数多态

需求:定义一个Person类,完成饲养猫,饲养狗的功能。设计猫和狗类的时候,使用继承完成。

Animal类:

package com.lanou.day05.demoduotai;

public class Animal {
    public String name; //姓名
    public Integer age;  //年龄

    public Animal() {
    }

    public Animal(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    //父类中的方法为了多态而存在,这个方法 一般空实现
    public void eat(){}
}

Cat:类

package com.lanou.day05.demoduotai;

public class Cat extends Animal{
    public Cat() {
    }

    public Cat(String name, Integer age) {
        super(name, age);
    }

    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

Dog类:

package com.lanou.day05.demoduotai;

public class Dog extends Animal{

    public Dog() {
    }

    public Dog(String name, Integer age) {
        super(name, age);
    }
    public void eat(){
        System.out.println("狗吃肉");
    }

    public void lookDoor(){
        System.out.println("狗看门");
    }
}

Person类:

package com.lanou.day05.demoduotai;

public class Person {

    private String name;
    private Integer age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void raisePet(Animal animal){
        //人可以饲养动物,动物会吃
        animal.eat();
    }
}

Test类:

package com.lanou.day05.demoduotai;

public class Test {
    public static void main(String[] args) {
        Person person = new Person("张三",22);
        Cat cat = new Cat("花花",1);
        person.raisePet(cat);

        Animal dog = new Dog("大黄",2);
        person.raisePet(dog);
    }
}

4.2 多态的应用——数组元素的多态

  1. 需求:创建一个数组,存放猫和狗。注意数组只能存放相同数据类型的数据。

  2. 代码:

public class ClassTest {
    public static void main(String[] args) {
        //创建猫和狗
        Cat c1 = new Cat(); 
        Cat c2 = new Cat(); 
        Cat c3 = new Cat();
        Dog d1 = new Dog(); 
        Dog d2 = new Dog(); 
        Dog d3 = new Dog(); 
        Cat[] arr = new Cat[6];
        arr[0] = c1;
        arr[1] = c2; 
        arr[2] = c3; 
        // arr[3] = d1;//报错 Cat数组只能存放Cat对象,不能存放Dog对象 
        // arr[4] = d2;//报错 Cat数组只能存放Cat对象,不能存放Dog对象 
        // arr[5] = d3;//报错 Cat数组只能存放Cat对象,不能存放Dog对象 
        Dog[] arr2 = new Dog[6]; 
        // arr2[0] = c1;//报错 Dog数组只能存放Dog对象,不能存放Cat对象 
        // arr2[1] = c2;//报错 Dog数组只能存放Dog对象,不能存放Cat对象 
        // arr2[2] = c3;//报错 Dog数组只能存放Dog对象,不能存放Cat对象 
        arr2[3] = d1; arr2[4] = d2; arr2[5] = d3;
        //创建Animal数组存放猫和狗。---多态
        Animal[] animals = new Animal[6]; 
        animals[0] = c1;//把Cat看成Animal存入数组 
        animals[1] = c2;//把Cat看成Animal存入数组 
        animals[2] = c3;//把Cat看成Animal存入数组 
        animals[3] = d1;//把Dog看成Animal存入数组 
        animals[4] = d2;//把Dog看成Animal存入数组 
        animals[5] = d3;//把Dog看成Animal存入数组 
        for(int i = 0; i < animals.length; i++) {
            Animal a = animals[i]; 
            a.eat();//多态的对象在运行期,执行子类的方法。
        } 
    }
}

4.3 多态的应用数组元素或方法返回值多态

  1. 需求:用Animal数组存放Cat和Dog。遍历数组,如果元素是猫,执行抓老鼠的方法,如果是狗执

行看门方法。

  1. 代码:猫类、狗类、动物类使用之前定义好的Cat、Dog以及Animal。
public class ClassTest {
    public static void main(String[] args) {
        //创建猫和狗
        Cat c1 = new Cat(); 
        Cat c2 = new Cat(); 
        Cat c3 = new Cat();
        Dog d1 = new Dog(); 
        Dog d2 = new Dog(); 
        Dog d3 = new Dog(); 
        //创建Animal数组存放猫和狗。---多态 
        Animal[] animals = new Animal[6]; 
        animals[0] = c1;//把Cat看成Animal存入数组 
        animals[1] = c2;//把Cat看成Animal存入数组 
        animals[2] = c3;//把Cat看成Animal存入数组 
        animals[3] = d1;//把Dog看成Animal存入数组 
        animals[4] = d2;//把Dog看成Animal存入数组 
        animals[5] = d3;//把Dog看成Animal存入数组 
        for(int i = 0; i < animals.length; i++) { 
            Animal a = animals[i]; 
            if(a instanceof Cat) {
                Cat c = (Cat)a;//强制类型转换 c.catchMouse();
            }else if(a instanceof Dog) {
                Dog d = (Dog)a; d.lookDoor(); 
            } 
        } 
    }
}

返回值多态:

动物管理员AnimalManager:

public class AnimalManager { 
    public Animal getAnimalByIndex(int index) { 
        Cat c1 = new Cat(); 
        Cat c2 = new Cat(); 
        Cat c3 = new Cat(); 
        Dog d1 = new Dog(); 
        Dog d2 = new Dog(); 
        Dog d3 = new Dog(); 
        Animal[] animals = new Animal[6];
        animals[0] = c1;
        animals[1] = c2;
        animals[2] = c3;
        animals[3] = d1;
        animals[4] = d2;
        animals[5] = d3;
        return animals[index]; 
    } 
}

测试代码:

public class ClassTest { 
    public static void main(String[] args) { 
        AnimalManager am = new AnimalManager(); 
        Cat a = (Cat)am.getAnimalByIndex(0);
        a.eat();
        a.catchMouse();
        Dog b = (Dog)am.getAnimalByIndex(3); 
        b.eat(); 
        b.lookDoor;
    } 
}

intanceof关键字的作用是:判断对象是不是某个类的实例。

例如:a instanceof Cat是判断对象a是否是Cat类的实例,如果是就返回true,否则就是false

如果a是Cat子类的实例,Cat继承于Animal,那么a instanceof Animal也是true。

向上转型:子类类型转换为父类类型。即把子类对象赋值给父类引用。系统自动完成。

向下转型:父类类型转换为子类类型。需要强制类型转换。

5. 包

包本质上就是文件夹。 有了文件夹,同名的类放到不同文件夹里,就能并存了。

包的最大价值 就是帮我们组织类。

不同类型的java类 放到 不同包中。

一个大项目通常有上百个类,或者几百个类。

如果所有的类都放到一个包里面,会出现2个问题

  • 这几百个类不能出现同名的类。因为同名的类会产生覆盖。
  • 查找某个(某些)类的时候不方便。因为包里的文件太多了。

如果要用的类,和当前类不在一个包里,需要通过import引入类。或者使用 完整限定名

什么时候必须使用完整限定名?
在一个类中,引入2个同名的类。
可以把一个类导入,另外一个类使用完整限定名。
A 包里有Test类   B包里有Student类,C包里有Student类。
Test类里既要用B包里的Student类,也要用C包里的Student类。
通过import 引入B包里的Student类
在使用C包里的Student类是,使用  C.Student stu = new C.Student()

这样就可以同时使用2个同名的类了。

导包的语法格式:

import 包名.类名; 或者import 包名.*;

6. finally

finally关键字:final单词的含义是最终的,在Java中final可以修饰类,方法,变量(实例变量,局部变量)。

6.1 finally修饰类

final修饰类:说明类是最终的类,即类不能有子类。换句话说就是类不能被继承。

package com.lanou.testfinal; 
public final class A { 
}
package com.lanou.testfinal; 
public class B extends A{//会报错 }

6.2 finally修饰方法

final修饰方法:说明方法是最终的方法,即方法不能被子类重写。

package com.lanou.testfinal;
public class AA {
    public final void test() {
        System.out.println("这是final修饰的方法,不允许被重写");
    } 
}
package com.lanou.testfinal;
public class BB extends AA {
    public void test() {
        //此处会报错! System.out.println("这是子类实现");
    } 
}

6.3 finally修饰变量

final修饰变量:说明变量是最终的,即变量的值不能发生变化。(如果变量没赋值,有一次赋值机会)。

package com.lanou.testfinal; 
public class AAA {
    private final int A = 100;
    public void method() {
        A = 200;//此处会报错! 
    }
}
package com.lanou.testfinal; 
public class TestFinal { 
    public static void main(String[] args) {
        final int A;
        A = 10;
        A = 20;//此处会报错 
    }
}

因为finally修饰的变量,值不能发生变化,所以我们通常把finally修饰的变量称为常量,变量名一般用纯大写命名。

7. static关键字

static单词的含义是静态的。在Java中static可以修饰属性、方法、代码块、内部类。—代码块、内部类以及static修饰他们。

7.1 static修饰属性

如果属性用static修饰,那么这个属性不再是实例变量,而是类变量。

实例变量,即实例的变量,有多少个实例就有多少个变量

类变量,即类的变量,这个类所有的实例共用这个变量。

package com.lanou.teststatic;
public class Person { 
    private String name; //姓名
    private int age; //年龄 
    public static String nationality; //国籍 
    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; 
    }
    public Person(String name, int age) { 
        super();
        this.name = name; 
        this.age = age; 
    }
    public Person() {
        super(); 
    }
    public void showInfo() {
        System.out.println(name + "," + age + "," + nationality);
    } 
}

package com.lanou.teststatic; 
public class PersonTest { 
    public static void main(String[] args) {
        Person p1 = new Person("张三", 21);
        p1.nationality = "中国";//p1设置国籍以后,p2也会是这个国籍 
        p1.showInfo(); 
        Person p2 = new Person("李四", 20); 
        p2.showInfo();
        p2.nationality = "美国";//p2改变国籍,p1也会改变国籍
        p2.showInfo(); p1.showInfo(); 
    }
}

被static修饰的属性,既可用对象访问,也可以用类访问。类属性应该使用类去访问,不要使用对象去访问(尽管可以)。

package com.lanou.teststatic; 
public class PersonTest { 
    public static void main(String[] args) { 
        Person p1 = new Person("张三", 21); 
        p1.nationality = "中国";//p1设置国籍以后,p2也会是这个国籍
        p1.showInfo();
        Person p2 = new Person("李四", 20);
        p2.showInfo(); 
        p2.nationality = "美国";//p2改变国籍,p1也会改变国籍
        p2.showInfo(); 
        p1.showInfo(); 
        Person.nationality = "朝鲜";
        p2.showInfo(); 
        p1.showInfo(); 
    }
}

7.2 static修饰方法

如果方法用static修饰,那么这个方法不再是实例方法,而是类方法。

实例方法:实例的方法,即对象的方法。由对象来调用方法。

类方法:类的方法,这个类所有的对象共用这个方法。

package com.lanou.teststatic; 
public class Person { 
    private String name; //姓名 
    private int age; //年龄 
    public static String nationality; //国籍 
    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; 
    }
    public Person(String name, int age) { 
        super(); 
        this.name = name;
        this.age = age; 
    }
    public Person() {
        super(); 
    }
    public void showInfo() { 
        System.out.println(name + "," + age + "," + nationality);
    }
    //静态方法(类方法) 
    public static void staticMethod() {
        System.out.println("这是静态方法。");
    } 
}
package com.lanou.teststatic;
public class PersonTest {
    public static void main(String[] args) { 
        Person p1 = new Person("张三", 21); 
        p1.staticMethod();//p1可以调用静态方法 
        Person p2 = new Person("李四", 20);
        p2.staticMethod()//p2也可以调用静态方法 
    } 
}

被static修饰的方法,既可以被对象访问,也可以被类访问**。类方法应该使用类去访问,不要使用对象去访问(尽管可以)。**

package com.lanou.teststatic;
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 21); 
        p1.staticMethod();//p1可以调用静态方法 
        Person p2 = new Person("李四", 20);
        p2.staticMethod();//p2也可以调用静态方法
        Person.staticMethod(); 
    } 
}

7.3 静态方法中不能访问实例变量

package com.lanou.teststatic;
public class Person { 
    private String name; //姓名 
    private int age; //年龄 
    public static String nationality; //国籍 
    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; 
    }
    public Person(String name, int age) {
        super(); 
        this.name = name;
        this.age = age;
    }
    public Person() { 
        super(); 
    }
    public void showInfo() {
        System.out.println(name + "," + age + "," + nationality); 
    }
    //静态方法(类方法)
    public static void staticMethod() { 
        System.out.println("这是静态方法。");
        System.out.println(name);//此处会报错! 
        name = "王五";//此处会报错! 
        showInfo();//此处会报错!
    } 
}

静态方法中,访问实例变量会报错。之所以报错是因为,静态方法是类方法,不是实例方法,因此访问实例变量的时候,不知道是访问的是哪个对象的实例变量。

静态方法中,访问实例方法会报错。之所以报错是因为,静态方法中访问实例方法,不知道访问的是哪个对象的实例方法。

静态方法中,不能使用this。this是一个代表当前方法调用者的对象。因为类通常是用类名调用的,不是具体的对象,所以不能用this。

4. static总结

  1. static修饰的属性和方法被所有对象共享,即可以被对象访问。

  2. static修饰的属性和方法可以被类访问。推荐使用类去访问静态属性和静态方法。

  3. 静态方法中只能访问静态属性和静态方法。

8. 代码块、静态代码块

在Java中一共3种代码块:

  1. 局部代码块

  2. 初始化代码块

  3. 静态代码块。

1. 局部代码块

定义在方法体内的代码块叫局部代码块。

public class BlockTest { 
    public static void main(String[] args) {
        int a = 10;
        {
            int b = 20; 
            System.out.println(a);
            System.out.println(b);
        }
        //System.out.println(b);//此处会报错。
    } 
}

上面的代码,在main方法中定义了一个局部代码块。代码块外定义的变量可以在代码块中使用,但代码块内定义的变量不能在代码块外使用,即代码块内的变量作用域仅限于代码块内。

**局部代码块平时很少使用。**switch…case中具体的case可以使用,可以避免多个case定义相同的变量出现重名的问题。

2. 初始化代码块

初始化代码块是定义在类中的代码块。它和属性、方法、构造方法属于是一个层级的东西。由于通常用

它来做初始化,所以叫做初始化代码块。

public class Person { 
    private String name;
    private int 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;
    }
    public Person() { 
        super();
        System.out.println("无参构造方法"); 
    }
    public Person(String name, int age) {
        super();
        this.name = name; 
        this.age = age; 
        System.out.println("全参构造方法");
    }
    { 
        name = "zhangsan"; 
        age = 20; 
        System.out.println("这是一个初始化块!我 比 构造方法执行 先执行");
    }
    public void showInfo() {
        System.out.println("姓名: " + name + ",年龄:" + age); 
    } 
}

测试代码:

public class BlockTest { 
    public static void main(String[] args) { 
        Person p = new Person();
        p.showInfo();
        Person p2 = new Person("lisi", 22);
        p2.showInfo(); 
    } 
}

初始化代码块作用

  1. 可以为属性赋值--------等价于构造方法。

  2. 可以做公共初始化。----如果多个构造方法,他们有共同的代码,共同的代码可以提到初始化代码

块中。

初始化代码块的特点

  1. 初始化代码块定义格式和方法类似,只不过只有{},没有参数,没有返回值。

  2. 初始化代码块不能主动调用,创建对象的时候自动执行,先于构造方法执行。

  3. 一个类中可以有多个初始化代码块,写在上面的初始化代码块先执行。尽管可以定义多个,一般最

多定义一个。

3. 静态代码块

用static修饰的代码块称为静态代码块。

静态代码块是类的代码块,随着类的加载而调用,因为类只会加载一次,所以静态代码块也只执行一

次。

public class StaticBlock { 
    public static int a = 100;
    private int b;
    public int getB() { 
        return b; 
    }
    public void setB(int b) { 
        this.b = b; 
    }
    //静态代码块
    static { 
        System.out.println("静态代码块"); 
    }
    //初始化代码块 
    { 
        System.out.println("初始化代码块"); 
    }
    //构造方法 
    
    public StaticBlock(int b) {
        super();
        this.b = b; 
        System.out.println("有参构造方法"); 
    }
    public StaticBlock() { 
        super(); 
        System.out.println("无参构造方法"); 
    } 
}

测试代码:

public class BlockTest { 
    public static void main(String[] args) {
        int num = StaticBlock.a;
        System.out.println(num);
        StaticBlock sb1 = new StaticBlock(); 
        StaticBlock sb2 = new StaticBlock(20); 
    } 
}

静态代码块的特点:

  1. 静态代码块的书写格式和初始化代码块的格式类似,在前面加一个static关键字即可。

  2. 静态代码块不能主动调用,类加载的时候自动调用。因为类只加载一次,所以只调用一次。

  3. 静态代码块可以有多个,写在上面的先执行。一般最多只写一个。

  4. 静态代码块中不可以使用实例变量,不可以调用实例方法。

  5. 静态代码块比初始化代码块先执行。

4. 继承关系里,代码的执行顺序

父类Father:

public class Father { 
    static { 
        System.out.println("父类--静态代码块"); 
    }
    { 
        System.out.println("父类----初始化代码块"); 
    }
    public Father() {
        super();
        System.out.println("父类------构造方法"); 
    }
}

Son类:

public class Son extends Father{ 
    static { 
        System.out.println("zi类--静态代码块"); 
    }
    { 
        System.out.println("zi类----初始化代码块"); 
    }
    public Son() { 
        super(); 
        System.out.println("zi类------构造方法");
    }
}

测试类:

public class BlockTest { 
    public static void main(String[] args) { 
        Son s = new Son();
        System.out.println("==========");
        Son s2 = new Son(); 
    } 
}

在继承关系里,创建子类对象时,会先加载类,再创建对象。

加载类时,先加载父类,再加载子类。— 类只有首次用的时候才加载,加载之后就一直在内存中。

创建对象时,先执行父类的初始化代码块和构造方法,再执行子类的初始化代码块和构造方法。

9. abstract类

abstract单词的含义是抽象。它可以用来修饰方法,也可以用来修饰类。被abstract修饰的类称为抽象类,被abstract修饰的方法称作抽象方法。

9.1 抽象类

抽象类最大的特点就是不能实例化对象,即不能创建对象

public abstract class Animal { 

}

测试代码:

public class ClassTest {
    public static void main(String[] args) {
        Animal a = new Animal();//会报错 
    } 
}

9.2 抽象类的作用

有些时候,父类只是为了被子类继承,在用的时候,我们希望别人用子类,而不是用父类,这个时候,我们就可以把父类定义成抽象类。

例如:Animal只是表示动物,即使创建了对象也没有什么意义,我们更希望创建的是猫、狗等更具体的

对象。为了防止别人误用,防止创建Aninal对象,我们可以通过abstract把Animal定义为抽象类,这样

别人就无法创建Animal对象了。

9.3. 抽象类中可以定义属性、方法吗?

能!一个普通类能包含的内容,抽象类都能包含。

抽象类和普通类的唯一区别就是不能创建对象。

public abstract class Animal { 
    //属性
    private String name; 
    private int age; 
    //常量 
    public static final int TEST = 100; 
    //静态属性 
    public static int test; 
    //setter、getter 
    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; 
    }
    //构造方法 
    public Animal() { 
        super();
    }
    public Animal(String name, int age) { 
        super();
        this.name = name; 
        this.age = age; 
    }
    //静态代码块 
    static { 
        System.out.println("静态代码块");
    }
    //初始化代码块 
    { 
        System.out.println("初始化代码块");
    }
    //实例方法 
    public void eat() { 
        System.out.println("吃东西");
    }
    //静态方法 
    public static void staticMethod() {
        System.out.println("静态方法"); 
    } 
}

9.4 抽象方法

抽象方法:被abstract修饰的方法。

抽象方法必须定义在抽象类中!

抽象方法不能有方法实现!

public abstract class Animal { 
    public abstract void eat();
}

9.5. 抽象方法有什么用?

**抽象方法是专门为多态设计的!**是专门设计用来被子类重写的!

Animal做为动物类,它比较抽象,即使实现了eat方法,也会被子类重写,不如不实现。只定义出来有这个一个方法,但不实现,由具体的子类来实现。

如果一个类继承于抽象类,那么必须实现抽象类中所有的抽象方法!或者自己也定义成抽象类,由自己的子类来实现抽象方法!Cat类实现Animal类的抽象方法:

public class Cat extends Animal { 
    @Override 
    public void eat() { 
        System.out.println("猫吃鱼"); 
    }
}

Cat类不实现Animal类的抽象方法:

public abstract class Cat extends Animal { 
}

子类BosiCat实现抽象方法:

public class BosiCat extends Cat { 
    @Override
    public void eat() { 
        System.out.println("波斯猫吃鱼");
    } 
}

9.6. 抽象类不能创建对象怎么办?

可以借助多态,用子类创建对象,抽象类的引用指向子类创建的对象。

public class ClassTest { 
    public static void main(String[] args) { 
        Animal a = new BosiCat(); 
        a.eat();
    } 
}

9.7 饲养宠物

  1. 需求:定义一个Person类,完成饲养猫,饲养狗的功能。设计猫和狗类的时候,使用抽象和继承

完成。

  1. 代码:

父类Animal:

public abstract class Animal { 
    private String name; 
    private int 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;
    }
    public Animal() { 
        super(); 
    }
    public Animal(String name, int age) {
        super();
        this.name = name;
        this.age = age; 
    }
    //声明抽象方法 eat 
    public abstract void eat(); 
    public void showInfo() {
        System.out.println(name + "," + age + "岁");
    } 
}

Cat类:

public class Cat extends Animal { 
    @Override
    public void eat() { 
        System.out.println("猫吃鱼"); 
    }
    public Cat() {
        super(); 
    }
    public Cat(String name, int age) { 
        super(name, age);
    }
}

Dog类:

public class Dog extends Animal { 
    @Override
    public void eat() { 
        System.out.println("狗吃骨头");
    }
    public Dog() { 
        super(); 
    }
    public Dog(String name, int age) {
        super(name, age);
    } 
}

Person类:

public class Person { 
    public void raiseAnimal(Animal animal) {
        animal.eat(); 
    } 
}

测试代码:

public class ClassTest {
    public static void main(String[] args) {
        Animal a1 = new Cat("花花", 2);
        Animal a2 = new Dog("旺财", 5);
        a1.eat();
        a2.eat(); 
        System.out.println("----------"); 
        Person p = new Person();
        p.raiseAnimal(a1); 
        p.raiseAnimal(a2);
    } 
}

总的来说:抽象类属于类的设计层面上的问题。它是为了设计类而存在,不是为了创建对象而存在。它最佳的使用场景就是多态。

抽象类声明子类必须要实现的方法(即自身的抽象方法),子类根据自己特征实现父类中声明的抽象方法。在创建和使用对象的时候,借助多态,用父类引用指向子类对象。这样可以充分发挥多态的优势。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王斐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值