Bootstrap

Java面向对象汇总

目录

面向对象基础

思想:

类、对象:

this关键字

构造器(构造方法)

封装(面向对象三大特征之一)

实体类(JavaBean)

局部变量和成员变量的区别

面向对象高级(一)

static

代码块

设计模式 - 单例设计模式

继承

面向对象高级(二)

多态

final

抽象类

设计模式 - 模板方法设计模式

接口

面向对象高级(三)

内部类

枚举

泛型


面向对象基础

思想:

  • 面向对象编程:拿或者找东西过来编程解决问题
  • 面向对象:把现实世界中的食物全部看成一个一个的对象来解决问题的。(万物皆对象)
  • 开发一个一个的对象,把数据交给对象,再调用对象的方法来完成对数据的处理
  • 好处:符合人类思维习惯,编程更简单,更直观

类、对象:

  • 类(设计图):相同事物共同特征的描述
  • 对象:对象是类的具体实例
  • 在Java中必须先定义类,才能创建对应的具体对象

定义类的格式:

修饰符 class 类名{	
	1.成员变量(代表属性,一般是名词)
	2.成员方法(代表行为,一般是名词)
	3.构造器(用来给对象的成员变量初始化,并返回对象的地址)
	4.代码块(待补充...)
	5.内部类(待补充...)
}

创建对象:

  • 格式:类名 对象名 = new 构造器(形参列表);
  • 对象调用:
    • 对象名.成员变量;
    • 对象名.成员方法;

类与对象的一些注意事项:

  1. 类名建议用英文单词,首字母大写,满足驼峰模式,且要有意义。
  2. 类中定义的变量也称为成员变量(对象的属性),类中定义的方法也成为成员方法(对象的行为)
  3. 成员变量本身存在默认值,在定义成员变量时一般来说不需要赋初始值。
  4. 一个代码文件中,可以写多个class类,但只能一个用public修饰,且public修饰的类名必须成为代码文件名
  5. .如果某个对象没有一个变量引用它,则该对象无法被操作,该对象会成为所谓的垃圾对象。而Java存在自动垃圾回收机制,会自动清除掉垃圾对象

this关键字

  • this就是一个变量,可以用在方法中,代表了当前对象的地址,可以访问当前对象的成员变量和成员方法
  • this主要用来解决:变量名称冲突问题等等

构造器(构造方法)

  • 作用:初始化一个类的对象,并返回这个对象的地址
  • 格式:修饰符 类名(形参){ ... }
  • 构造器的分类:
    • 无参数构造器:初始化一个类的对象,并返回这个对象的地址,里面的数据都是默认值
    • 有参数构造器:初始化一个类的对象,并返回这个对象的地址,并且可以同时为对象赋值进行初始化
  • 构造器的调用:类名 对象名 = new 构造器();
  • 注意:
    • 任何类写出来JVM都默认配置一个无参数构造器
    • 若自己在类中以及定义了一个有参数构造器了,那么JVM就不再默认配置无参数构造器,需要自行再配置一个。

封装(面向对象三大特征之一)

面向对象三大特征:封装、继承、多态(继承、多态内容待补)

  • 封装的设计规范:合理隐藏,合理暴露
    一般来说:
    • 对象的成员变量(对象的属性)都private,只能本类访问,然后设计相应的setter、getter方法来赋值和取值
    • 而对象的成员方法则根据实际情况进行隐藏(private)或暴露(public)。

实体类(JavaBean)

  • 实体类(JavaBean)是一种特殊形式的类
  • 作用:创建对象,仅仅用来封装保存数据,实现存储数据(JavaBean实体类)与数据处理业务(另一个类)的分离
  • 标准bean的书写要求:
    • 实体类中的成员变量都要私有化(private),并且要对外提供相应的getter,setter方法
    • 类中必须要有一个公共的无参的构造器

局部变量和成员变量的区别

  • 类中位置不同,成员变量(类中,方法外),局部变量(常见于方法中)
    int a = 20; //main方法中 局部变量
  • 初始化值不同,成员变量(有默认值,不需要初始化),局部变量(没有默认值,使用之前必须完成赋值)
  • 内存位置不同成员变量(堆内存中),局部变量(栈内存中)
    • 成员变量属于new出来的对象的,而对象在堆内存,故成员变量也在堆内存
    • 局部变量在方法中,方法在栈内存中运行,故局部变量也在栈内存
  • 作用域不同,成员变量(整个对象),局部变量(在所属的大括号中)
  • 生命周期不同:成员变量(与对象同生共死),局部变量(方法调用而生,方法结束而亡)

面向对象高级(一)

static

  • 叫静态,可以修饰成员变量,成员方法
  1. 成员变量按照有无static修饰,分为两种:
  • 类变量:有static修饰,属于类,在计算机里只有一份,会被类的全部对象共享
    • 访问:类名.类变量(推荐) 对象.类变量(不推荐)
  • 实例变量(对象的变量):无static修饰,属于每个对象
    • 访问:对象.实例变量

类变量的应用场景:

  • 在开发中,如果某个数据只需要一份,且希望能够被共享(访问,修改),则该数据可以定义成类变量来记住

成员变量的应用场景:

  • 每个对象都要有一份,数据各不同

ps:

a.访问自己类中的类变量可以省略类名不写

b.一般类变量用public修饰,方便别人共享

2.成员方法的分类:

  • 类方法:有static修饰的成员方法,属于类。
    • 访问:类名.类方法(推荐) 对象名.类方法(不推荐)
  • 实例方法:无static修饰的成员方法,属于对象。
    • 访问:对象.实例方法

类方法的应用场景:

  • 做工具类(用来完成一个功能,给开发人员共同使用的)
    • 好处:提高了代码复用性,调用方便,提高了开发效率
    • 为什么工具类中的方法要用类方法,而不用实例方法?
      • 实例方法需要创建对象来调用,此时对象只是为了调用方法,对象占内存,这样会浪费内存
      • 类方法,直接用类名调用即可,调用方便,也能节省内存。
    • ps:工具类没有创建对象的需求,建议将工具类的构造器进行私有,以防止创造对象占用内存。

使用类方法、实例方法时的几点注意事项:

  • 类方法中可以直接访问类成员,不可以直接访问实例成员。
  • 实例方法中既可以直接访问类成员,也可以直接访问实例成员
  • 实例方法中可以出现this关键字,类方法中不可以出现this关键字

代码块

代码块概述:

  • 代码块时类的5大成分之一(成员变量,构造器,方法,代码块,内部类)

代码块种类:

  • 静态代码块:
    • 格式:static{ }
    • 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
    • 作用:完成类的初始化,例如:对类变量的初始化赋值
  • 实例代码块:
    • 格式:{ }
    • 特点:每次创建对象时,执行实例代码块,并在构造器前执行
    • 作用:和构造器一样,都是用来完成对象的初始化,例如:对实例变量进行初始化赋值

设计模式 - 单例设计模式

设计模式概念:

  • 具体问题的最优解法

单例设计模式

  • 确保一个类只有一个对象

写法:

  • 把类的构造器私有化
  • 定义一个类变量记住类的这一个特定对象
  • 定义一个类方法返回对象

类只会加载一次,类变量也只会随之加载一次,即只会创建一次对象,以确保一个类只有一个对象

单例设计模式的实现方式:

  • 懒汉式单例:拿对象时,对象早就创建好了
/**
 * 单例设计模式 - 饿汉式单例
 */
public class A {
    //单例类

    //2.定义一个类变量记住类的一个对象
    private static A a = new A();

    //1.把类的构造器私有,外部类访问不到
    private A(){

    }

    //3.定义一个类方法,返回类变量所记住的该类的对象
    public static A getObject(){
        return a;
    }
}
  • 饿汉式单例:拿对象时,才开始创建对象
public class B {
    //懒汉式单例

    //2.创造一个类变量使它指向null
    private static B b = null;

    //1.将构造器private私有化
    private B(){

    }

    //3.提供一个类方法,保证返回的是同一个对象
    public static B getInstance(){
        if(b == null){
            System.out.println("第一次创建对象");
            b = new B();
        }
        return b;
    }
}

继承

继承定义:

  • Java提供了一个extends关键字,可以让一个类与另一个类建立起父子关系

继承的特点:

  • 子类能继承父类的非私有成员(成员变量,成员方法)

继承后对象的创建:

  • 子类的对象是由父类,子类共同完成的

继承好处:

  • 减少了重复代码的编写,提高了代码的复用性

继承的相关事项:

  1. 权限修饰符:

修饰符

在本类中

同一个包下的其他类

任意包下的子类里

任意包下的任意类

private

缺省

protected

public

  1. 单继承、Object类:
    • Java是单继承的,Java中的类不支持多继承,但是支持多层继承
    • object类是Java所有类的祖宗类
  1. 方法重写(申明不变,重新实现):
    • 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写
    • 重写小技巧:使用Override注解,它可以指定Java编译器,检查我们方法重写的格式是否正确
    • 子类重写父类方法时,访问权限必须大于或者等于父类该方法的访问权限(public > protected > 缺省)
    • 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小
    • 私有方法,静态方法不能被重写,会报错
  1. 子类中访问其他成员的特点:
    • 就近原则:先子类局部范围找,再子类成员范围找,最后父类成员范围找
    • 重名:调用子类成员(this.),调用父类成员(super.)
  1. 子类构造器的特点:
    • 子类的全部构造器,都会先调用父类的构造器,再执行自己
      • 默认情况下,子类全部构造器的第一行代码都是super()(写不写哦都有),它会调用父类的无参构造器
      • 如果父类没有无参数构造器,则默认报错。此时我们必须在子类构造器的第一行手写super(...),指定去调用父类的有参数构造器

  1. 注意事项的小结:
    • 补充知识:this(...)调用兄弟构造器
    • this(...)和super(...)使用时的注意事项:
      • this(...)、super(...)都只能放在构造器的第一行,因此,有了this(...)就不能写super(...)了,反之亦然

面向对象高级(二)

多态

多态定义:

  • 多态是在继承/实现情况下的一种现象,表现为对象多态、行为多态

多态的具体代码实现:

public class Test {
    public static void main(String[] args) {
        //目标:认识多态,对象多态,行为多态
        //1.对象多态
        People p1 = new People();
        p1.run();//行为多态。识别技巧:编译看左边,运行看右边
        System.out.println(p1.name);//注意:对于变量,编译看左边,运行也看左边

        People p2 = new Student();
        p2.run();
        System.out.println(p2.name);

        People p3 = new Teacher();
        p3.run();
        System.out.println(p3.name);
    }
}

运行结果:
人可以跑~
父类People
学生跑的贼快~
父类People
老师跑的气喘吁吁
父类People

多态的前提:

  • 有继承/实现关系;存在父类引用子类对象(对象多态);存在方法重写(行为多态)

多态的注意事项:

  • 多态是对象、行为(方法)的多态,Java中的属性(成员变量)不谈多态(编译和运行一律看变量的类型)

多态的好处和问题以及解决方法(类型转换):

  • 好处1:可以实现解耦合(组件互相独立,可以随时切换),右边对象可以随时切换,后续业务随机改变

        People p1 = new People();// new Student(); new Teacher();...
        p1.run();

  • 好处2:定义方法时,可以使用父类类型的变量作为形参,可以接收一切子类对象,扩展性更强
  • 问题:无法直接调用子类的独有功能
    • 解决方法:类型转换
      • 自动类型转换:父类 变量名 = new 子类();
People p = new Student();

      • 强制类型转换:子类 变量名 = (子类) 父类变量
Student s = (Student)p;

      • 强制类型转换的一个注意事项:
        • 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译不会报错
        • 但运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常
People p = new Student();
Teacher t = (Teacher) p;//java.lang.ClassCastException
      • 强转前,Java建议:使用instanceof关键字,判断当前对象的真实类型,再进行转换,进而调用子类的独有方法
p instanceof Student //true

综合代码:

public class Test {
    public static void main(String[] args) {
        //目标:理解多态的好处
        //好处1:可以实现解耦合(组件互相独立,可以随时切换),右边对象可以随时切换,后续业务随机改变
        People p1 = new People();// new Student(); new Teacher();...
        p1.run();

        //好处2:定义方法时,可以使用父类类型的变量作为形参,可以接收一切子类对象,扩展性更强
        Student s1 = new Student();
        go(s1);

        Teacher t1 = new Teacher();
        go(t1);
		
		//问题:无法直接调用子类的独有功能
        People p = new Student();
//        p.study();//多态下存在问题,无法直接调用子类的独有功能
        //强制类型转换
        Student s = (Student) p;
        s.study();
        //强制类型转换可能存在问题,编译阶段有继承或者实现关系就可以进行强制转换,但是运行时可能出现类型转换异常
        Teacher t = (Teacher) p;
        t.teach();//运行时,发现对象的真实类型与强转后的类型不同,报错ClassCastException
        //此时可以使用 instance 关键字来判断对象的真实类型,再进行强转,从而调用子类的独有功能
    }

    public static void go(People p){
        p.run();
        if(p instanceof Student){
//            ((Student) p).study();
            Student s = (Student) p;
            s.study();
        } else if (p instanceof Teacher){
//            ((Teacher) p).teach();
            Teacher t = (Teacher) p;
            t.teach();
        }
    }
}

final

  • final关键字是最终的意思,可以修饰(类、方法、变量)
    • 修饰类:该类被称为最终类,特点是不能被继承了
    • 修饰方法:该方法被称为最终方法,特点是不能被重写了
    • 修饰变量:该变量仅能被赋值一次

final修饰变量的注意

  • final修饰基本数据类型的变量,变量存储的数据不能改变
  • final修饰引用数据类型的变量,变量存储的地址不能被改变,但地址所指向的对象的内容是可以被改变的

常量

  • 使用了static final修饰的成员变量就被称为常量
  • 作用:通常用于记录系统的配置信息

public class Constant{
	public static final String SCHOOL_NAME = "黑马";
}

==注意:==常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接

使用常量记录系统配置信息的优势、执行原理

  • 代码可读性更好,可维护性也更好
  • 程序编译后,常量会被“宏替换”“出现常量的敌方全部被替换成其记住的字面量,这样可以保证使用常量和直接使用字面量的性能是一样的

抽象类

  • abstract关键字,可以修饰(类、成员方法) --> 抽象类、抽象方法
    • 抽象方法:必须abstract修饰,只有方法签名,不能有方法体

修饰符 abstract class 类名{
	修饰符 abstract 返回值类型 方法名(形参列表);
}

抽象类的注意事项、特点

  • 抽象类中不一定有抽象方法、但有抽象方法的类一定是抽象类
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有
  • ==抽象类最主要的特点:==抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
  • 一个类继承抽象类,必须要重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类

抽象类的应用场景和好处

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们抽出这样的抽象类,就是为了更好的支持多态

tips:

  • abstract不可和static | private连用,因为后两者修饰的方法不支持重写,而abstract抽象方法在子类继承时需要将其重写

设计模式 - 模板方法设计模式

  • 解决方法中存在重复代码的问题

写法

  • 1.定义一个抽象类
  • 2.在里面定义2个方法
    • 一个是模板方法:把相同代码放里面去
    • 一个是抽象方法:具体实现交给子类完成
    • 最后将二者关系匹配联系起来

/**
 * 设计 模板方法设计模式
 * 1.定义一个模板方法出来
 */
public abstract class People {
    //模板方法,所有子类直接套用即可,不能更改
    public final void write(){
        System.out.println("\t\t\t《作文题目》");
        System.out.println("\t\t第一段大同小异");
        //2.模板方法并不清楚正文部分应该具体怎么写,但是它知道子类肯定要写,故此处调用一个抽象方法
        System.out.println(writeMain());
        System.out.println("\t\t最后一段大同小异");
    }

    //3.设计一个抽象方法,具体实现交给子类子类,调用它时写正文
    public abstract String writeMain();
}

tips:

  • 建议使用final关键字修饰模板方法
    • 模板方法是给对象直接使用的,不能被子类重写
    • 一旦子类重写了模板方法,模板方法就失效了

接口

  • interface关键字定义接口
  • 结构:

public interface 接口名{
	//成员变量(JVM默认成常量)
	//成员方法(JVM默认成抽象方法)
	
	//JDK8后出现的3种方法:
	//默认方法:(public)default 返回值类型 方法名(){...}
//    	注意:只能使用接口的实现类的对象调用
	//私有方法:private 返回值类型 方法名(){...}
 //    	注意:只能接口内部调用使用
	//静态方法:(public)static 返回值类型 方法名(){...}
 //    	注意:只能使用接口名.方法名来访问,
    
 	优点:增强了接口的能力,更便于项目的扩展和维护
}

tips:

  • 接口中不能有构造器,代码块啥的
  • 接口大多数成员都是默认public修饰的,因为接口要供别人(类,接口)使用
  • 接口不能创建对象(因为接口是抽象的),接口是被类用来==实现(implements)==的,实现接口的类称为实现类

修饰符 class 实现类 implements 接口1,接口2...{
	
}

  • 一个类可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类

接口的好处

  • 弥补了类单继承的不足,一个类同时可以实现多个接口,从而去扩展自己的功能
  • 让程序可以面向接口编程,更加灵活方便的切换各种业务实现

接口的多继承

interface B{}
interface C{}
interface A extends B,C{}//相当于把B、C接口合并了,便于实现类去实现

接口其他注意事项(了解)

public class Test2 {
    public static void main(String[] args) {
        new Zi().run();
    }
}
//1.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承
interface I{
    void test();
}
interface J{
    String test();
}
//interface K extends I,J{}

//2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现
//class E implements I,J{}

//3.一个类继承了父类,又同时实现了接口,父类和接口中有同名的默认方法,实现类会优先用父类的
class Fu{
    public void run(){
        System.out.println("父类的run方法~");
    }
}
interface IT{
    default void run(){
        System.out.println("接口的run方法");
    }
}
class Zi extends Fu implements IT{

}

//4.一个类实现多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写这个方法即可
interface A1{
    default void test(){
        System.out.println("test");
    }
}
interface A2{
    default void test(){
        System.out.println("test");
    }
}
class A3 implements A1,A2{

    @Override
    public void test() {
        System.out.println("需要重写同名的默认方法");
    }
}

面向对象高级(三)

内部类

  • 内部类是类中的五大成分之一(成员变量,方法,构造器,内部类,代码块),如果一个类定义在另一个类的内部,这个类就是内部类
  • 当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类
public class Car{
    //内部类
    public class Engine{
    
    }
}

四大内部类

1.成员内部类

就是类中的一个普通成员,类似前面学过的普通的成员变量,成员方法,需要外部类的对象去调用

public class Outer{
    //成员内部类
    public class Inner{

    }
}

创建内部类的对象的格式:

外部类名.内部类名 对象名 = new 外部类名().new内部类名();
Outer.Inner in = new Outer().new Inner();

成员内部类中访问其他成员的特点

和前面学过的实例方法一样,成员内部类的实例方法中,同样可以直接访问外部类的实例成员和静态成员

可以在成员内部类的重写方法中,拿到当前外部类对象,格式是:外部类名.this

2.静态内部类

有static修饰的内部类,属于外部类自己持有

直接由外部类名调用

public class Outer{
    //静态内部类
    public static class Inner{
    
    }
}

创建对象的格式:

外部类名.内部类名 对象名 = new 外部类.内部类();
Outer.Inner in = new Outer.Inner();

静态内部类中访问外部类成员的特点

可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员

3.局部内部类

4.匿名内部类

一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字

new 类或接口(){
    类体(一般是方法重写);
};

new Animal(){
    @Override
    public void cry(){
        ...
    }
};

特点:匿名内部类本质就是一个子类(实现类),由于调用了构造器会立即创建出一个子类(实现类)对象

作用:用于更方便的创建一个子类(实现类)对象

最终目的:简化代码

一般都是api需要我们使用匿名内部类去调用,自己写程序很少主动使用匿名内部类

枚举

  • 枚举是一种特殊类

枚举的格式

修饰符 enum 枚举类名{
    名称1,名称2...
    其他成员...
}

public enum A{
    X,Y,Z;
    ...
    
}

注意:

  • 枚举类红的第一行,只能协议发合法的标识符(名称),多个名称用逗号隔开
  • 这些名称,本质是常量,每个常量都会记住枚举类的一个对象
  • 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象
  • 枚举都是最终类,不可以被继承
  • 枚举类中,从第二行开始,可以定义类的其他各种成员
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法
  • 枚举类本身可以改造为一种单例模式

枚举的常见应用场景:

泛型

  • 定义类、接口、方法时,同时声明了一个或者多个类型变量(如<E,T>),称为泛型类、泛型接口、泛型方法,它们统称为泛型
public class ArrayList<E>{
    ...
}
  • 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常
  • 泛型的本质:把具体的数据类型作为参数传给类型变量

泛型接口是给实现类实现的,实现类实现泛型接口的时候可以申明一个数据类型,实现类重写的方法都是针对该类型的操作。

泛型方法:

修饰符<类型变量,类型变量...>返回值类型 方法名(形参列表){

}

public static <T> void test(T t){
    
}

通配符:

  • 就是"?",可以在 使用泛型 的时候代表一切类型,E T K V 是在定义泛型的时候使用

泛型的上下限:

  • 泛型上限:? extends Animal ?能接收的必须是Animal或其子类
  • 泛型下限:? super Animal ?能接收的必须是Animal或其父类

理解泛型的注意事项:1.泛型是工作在编译阶段的,在编写代码时就纠错,一旦编译成class文件,class文件中就不存在泛型了,这就是泛型擦除2.泛型不支持基本数据类型(但支持基本数据类型对应的包装类),只支持对象类型(引用数据类型)

;