Java面向对象

面向对象

        要理解面向对象思想,就先要知道什么是对象?

        对象,不只是“男女朋友”,在《Java编程思想》中多次提到“万物皆对象”的概念。它除了可以存储数据之外还可以对它自身进行操作。它能够直接反映现实生活中的事物,例如人、车、小鸟等一切事物,都可以表示为程序中的对象。每个对象都具有各自的状态特征(也可以称为属性)及行为特征(方法),java就是通过对象之间行为的交互来解决问题的。

        面向对象就是把构成问题的事物分解成一个个对象,建立对象不是为了实现一个步骤,而是为了描述某个事物在解决问题中的行为。

        语法:类名 对象名 = new 类名()

面向对象基础

this
  • this是一个关键字,用在方法中,作用是拿到调用者对象

  • 谁调用方法,调用者对象就是谁,方法里的this指代的就是谁

可见性修饰符
  • 可见性修饰符

    • 可见性修饰符也叫权限修饰符,是用来约束类中成员的可见范围。

  • 最常用的可见性修饰符

    符号翻译同一个类中同一个包下的其他类中不同包下的类中
    private私有的
    缺省(package-private)包私有的
    public公共的
构造方法
  • 构造方法

    • 构造方法是一种特殊的方法,特殊之处在于不能写返回值类型,且名称必须保持一致。

  • 定义构造方法

    • 无参构造方法

      • 类名 对象名 = new 类名();

    • 有参构造方法

      • 类名 对象名 = new 类名(参数);

  • 构造方法的作用

    • 用来初始化一个类的对象,并返回对象的地址。

  • 构造方法的特点

    • 有参数构造方法:在初始化对象的时候,同时可以接收参数为对象的成员赋值

    • 无参数构造方法:初始化对象时,成员变量的数据采用默认值。

  • 不同类型成员变量默认规则

    数据类型默认值
    byet/short/int/long/char0
    double/float0.0
    booleanfalse
    引用数据类型null
  • 构造方法的注意事项:

    1. 任何定义出来的,默认就自带了无参数构造方法,即使没有写也存在。

    2. 一旦定义了有参数构造方法,默认的无参数构造方法就没有了,如果还想用无参数构造方法,需要自己写出来。

    3. 构造方法的名字必须和类名保持一致。

    4. 定义构造方法的时候一定不能写返回值类型,void也不行。

方法重写
  • 当子类继承自父类的方法不能完全满足需求时,可以在子类中重新编写继承写自父类的方法,这个过程就叫做方法重写。

怎样进行方法重写

  • 要重写父类中的哪个方法,就在子类中编写一个签名(返回值类型、方法名、形参列表)和父类中被重写放一模一样的方法。

@Override

  • 标记当前方法是重写的方法,同时可校验重写的语法是否正确

注意:如果==左右两边是引用数据类型(对象),比较的是两个对象的地址值是否相同!如果相同,证明是同一个对象,否则则不是。

实际开发中,调用对象的equals方法往往是要比较对象的内容而不是地址值,因此要重写equals方法。

实体JavaBaen
  • 实体JavaBaen

    • 实体JavaBaen也叫实体类,本质上就是一个满足封装原则的类,其对象可以用于在程序中封装数据。

  • 实体JavaBaen的书写要求

    1. 类中的成员变量全部私有,并提供public修饰的getter/setter方法

    2. 类中至少提供一个无参数构造方法,有参数构造方法可选,建议两个都提供

三大特征

封装
  • 程序中的封装
    • 程序中的封装指的是在设置类时,将类中的成员变量和成员方法的实现细节隐藏起来,再对外暴露一个使用说明书

  • 在程序中封装

    • 类中的成员变量使用pirvate修饰,确保成员变量只能在本类中访问

    • 为每个成员变量提供一个public修饰的getter方法来返回它的值和一个public修饰的setter方法来修改它的值

继承
  • 面向对象编程允许根据一个现有的类定义出新的类,这个特性叫做继承
  •     继承的语法
    • 在继承关系下,至少要有两个类,一个是比较通用的类,叫做父类;另一个是更具体的    类,叫做子类。父类和子类通类过extends关键字建立起父子关系。

    • public class 子类 extends 父类{}

  • 继承的作用

    • 子类继承父类后,子类和子类对象可以直接使用父类中所有非私有的属性和方法。

  • 继承的特点

    • 单继承

      •  Java是单继承模式:一个类只能继承一个直接父类

    • 多层继承

      • Java不支持多继承,但支持多层继承

    • 最终类

      •  任何一个类既可以继承其他类,也可以被其他类来继承,如果某个类不想被其他类    继承,可以使用final修饰

    • 祖宗类

      • Java中所有类,要么直接继承了Object ,要么默认继承了Object ,要么间接继承了        Object, Object是所有类的祖宗类。

多态
  • 面向对象编程允许一个父类变量指向它的任何一个子类对象,这个特性叫做多态。
    •  由于一个父类可以有多个不同的子类,父类变量可以有多种不同的形态,这是多态的字    面理解。
  • 多态的语法
    •  父类 对象名称 = new 子类()
    •  在继承关系下,创建一个子类对象,再把它赋值给它的父类类型的变量,这样就实现了     多态
  • 多态下方法调用的特点
    • 执行看左边,运行看右边
  • 使用多态的好处
    • 在多态的形式下,可以非常方便地替换等号右边的对象,便于程序的扩展和维护。
    • 定义方法的时候,使用父类类型作为参数,调用方法的时候可以传递父类对象,也可以          传递它的一切子类对象,方法更通用。
  • 多态下的类型转换
    • 自动类型转换
      • 父类 对象名称 = new 子类()
    • 强制类型转换
      • 多态下的强制类型转换将父类对象赋值给子类类型的变量时,需要在父类对象前指    定子类类型。
      • 子类类型 子类对象名 = (子类类型)父类对象名
      • 强转的好处:将父类对象强转成子类类型后,就可以调用子类中的独有功能了。
      •  强转的注意事项:只要是有继承/实现关系的类就可以在编译阶段进行强制类型转换;但是,如果转型后的类型不是对象的真实类型,那么在代码运行时,就会出现ClassCasException

        Animal a = new Tortoise();
        Dog d = (Dog) a;

      • 规范做法:在强转之前使用instanceof 关键字进行判断对象的真实性,如果是对应的类型再强转。
        Animal a = new Tortoise();
        if(a instanceof Dog){
            Dog d = (Dog) a;
        }

三大思想

OOA

        面向对象分析(Object Oriented Analysis)

OOD

        面向对象设计(Object Oriented Design)

OOP

        面向对象程序(Object Oriented Programming )

static关键字

static概述

  • static是Java中的一个关键字,是静态的意思,可以用来修饰成员变量、成员方法、初始化块。

  • static修饰的成员变量成为静态成员变量(类变量),static修饰的修饰方法成为静态成员方法(类方法)。

  • 被static修饰的成员都是属于类的,随着类的加载而加载,在内存中只有一份,并且可以直接用类名访问。

static修饰成员方法

  • static修饰成员方法的应用场景

    • 类中的方法的计算逻辑和对象无关,测试类中创建学生对象仅仅是为了调用方法,使用不方便,且会浪费内存!建议使用static来修饰方法。

  • 使用static修饰成员方法

    • static是静态的意思,被static修饰的成员属于类,随着类的加载只加载一次,推荐直接使用类名访问

    • 注意:static修饰的成员可以被类的所有对象共享,因此,也可以用对象访问它们,但不推荐这样做(防止代码冗余,还会占内存)

工具类

  • 什么是工具类?

    • 工具类是一种特殊的类,类中的所有方法都有static修饰,直接用类名就可以调用这些方法完成一定的功能,每个方法用起来都像使用小工具一样简单,因此,把这样的类称为工具类。

  • 一个典型的工具类

    • JDK中提供了一个和数学运算相关的工具类,叫做Math,类中都是一些和数学运算相关的静态方法。

      快捷键功能说明快捷键功能说明
      static double asb(double a)获取参数的绝对值static double max(double a, double b)获取两个数字的较大值
      static double ceil(double a)向上取值static double min(double a, double b)获取两个数字的较小值
      static double floor(double a)向下取整static double pow(double a, double b)获取a的b次方的值
      static long round(double a)四舍五入static double sqrt(double a)获取参数的平方根
  • 为什么Math类中的构造器要使用private修饰?

    • 工具类中的方法都是static修饰的静态方法,直接用类名调用就可以了,不需要创建对象。将构造器私有可以防止使用者创建对象调用静态方法,从而造成内存浪费

static修饰成员变量

静态成员变量通常使用public修饰,方便共享。

可以被类的所有对象共享,内存中只有一份

直接用类名.静态成员变量名访问

  • static修饰成员变量的应用场景

    • 如果类中的某个成员变量的值需要在类的所有对象之间共享,建议用static修饰该成员变量。

常量

  • 常量

    • 在Java中,常量在执行过程中值不能改变静态成员变量,比如圆周率π,在程序的整个执行过程中,只能等于3.14

    • 常量命名规范:英文单词全部大写,多个单词用下划线链接

    • 定义常量是使用final关键字修饰静态成员变量可以保证它的值不变

    • final修饰的变量,一旦定义就不能修改,所以要在定义时直接赋值

  • 常量的使用

    • 类名.常量名

  • Math工具类的常量

    • PI(圆周率)

    • 在程序中要使用圆周率可以使用Math工具类:Math.PI;

初始化块和静态初始化块

  • 初始化块

    • 在Java类中,使用{ }括起来代码块被称为初始化块儿,定义在类中方法外,作用是对所有对象进行统一的初始化操作。

    • 初始化中的代码在每次调用构造器初始化对象前执行

    • 不同对象的成员属性值往往都是不同的,因此,初始化块儿在实际开发中几乎不用

  • 静态初始化块

    • 静态初始化块儿是被static修饰的初始化块,语法格式:static{ }

    • 静态初始化块通常用来在程序启动时做一些初始化静态数据的操作,随着类的加载而加载,并且自动触发,只执行一次

  • 静态初始化的应用场景

    • 有些场景下,类启动的时候无法直接确定成静态成员变量的值,或者要进行一些计算才能得到的静态成员变量的值。

使用static的注意事项

  1. 静态方法中只能访问静态的成员,不能访问实例成员。

  2. 实例方法中可以访问静态成员,也可以访问实例成员。

  3. 实例方法中可以使用this关键字,静态成员方法中不能使用this关键字。

  4. 上述三条注意事项也适用于初始化块。

抽象

抽象类

Java提供了一个关键字:abstract,是抽象的意思,可以用来修饰类,也可以用来修饰方法。

被abstract修饰的类就是抽象类;被abstract修饰的方法就是抽象方法

具体语法为:

        可见修饰符 abstract 类名{

                可见性修饰符 abstract 方法返回值类型 方法名(形参列表)

        }

定义抽象类和抽象方法的注意事项

  • 类中能书写的代码(成员变量,成员方法,构造方法等),抽象类中也能。

  • 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类

  • 抽象方法只有签名,不能写方法体

抽象类的使用场景

  • 抽象类可以理解成类的不完整的设计图,一般作为父类,让子类来继承。因此,抽象类也叫抽象父类

  • 当多个类都要完成某些行为,但每个类的该行为实现又不相同时,就可以把该行为定义成抽象父类中的抽象方法,抽象方法的具体实现交给子类来完成

使用抽象类的注意事项

  • 一个类如果继承了抽象类,必须重写完抽象类中的全部抽象方法,否则这个类也必须定义成抽象类。

  • 不能创建抽象对象。

抽象类的构造方法的作用

  • 抽象类的构造方法是给子类构造方法用的。

子类构造方法中使用抽象类构造方法的作用

  • 子类继承抽象类父类时,也会继承父类中的成员属性,在初始化子类对象时,一定要先初始化这些继承自父类的属性;而父类中定义的属性要使用父类构造方法初始化,因此,在子类构造方法中初始化子类数据前,一定要先调用父类构造方法。

子类构造方法中怎样使用父类构造方法

  • 子类所有构造方法的第一行默认都有一行代码:super(参数);即使不写也存在、
  • super()的作用是调用父类的无参构造方法,初始化继承父类的数据
  • 如果想调用父类的有参构造方法,在小括号中传参数即可:super(参数);

接口

接口的概述

  • 接口

    • Java提供了一个关键字:interface,用这个关键字定义出来的结构就是接口。

    • 语法具体为:

      可见性修饰符 interface 接口名{

      0~多个抽象方法

      }

    • 接口可以看成是一种规范,约束实现接口的类必须具备某种能力

  • 怎样使用接口

    可见性修饰符 class 类名 implements 接口名1,接口名2…{

    在实现类中必须重写实现的全部接口中的全部抽象方法

    }

    • 接口是用来被类实现的

      • 一个类可以实现一个接口,也可以实现多个接口,类与接口的关系是多实现

Arrays工具类

  • 数组为了方便操作数组中的数据,Java提供了一个工具类,名字是Arrays。
方法签名作用
public static String toString (Object[] a)返回任意类型(包括基本数据类型)数组内容的字符串表示
public static void sort (Object[] a)对任意类型(包括基本数据类型)的数据进行排序
  • 直接用Arrays类的sort方法对自定义类型数组排序会报错

  • 让实体类实现Comparable接口,重写comparTo方法,指定比较规则

    • 排序规则:

      • return 比较对象的值 - 被比较对象的值:升序

      • return 被比较对象的值 - 比较对象的值:降序

使用接口的好处

  • 弥补了类单继承的不足,一个类在继承一个父类的同时还可以实现多个接口。

    可见性修饰符 class extends 父类名 implements 接口1,接口2…{

    在实现类中必须重写类实现的全部接口中的全部抽象方法

    }

  • 一个类可以实现多个接口,同样地,一个接口也可以被多个类实现,这多个实现类都可以看成是接口的子类,结合多态思想,可以让程序面向接口编程,将来程序可以灵活方便的切换各种业务实现。

    接口名 对象名称 = new 实现类名();

    对象名.方法名

JDK8中接口中新增的方法

  • 在JDK8以前的版本中,接口中定义的方法只能是抽象方法。

  • 允许接口中直接定义带有方法体的方法

  • 项目Version2.0需要对Inter接口进行增强,加入10个新的抽象方法,此时改了接口就要所有实现类实现这些方法。

  • JDK8中新增的默认方法

    • 接口中的默认方法类似于类中的普通实例方法,不同之处在于需要使用default修饰。默认方法默认都有public修饰。

    • 调用方式:使用接口实现类的对象来调用,实现类中可以重写默认方法,也可以不重写默认方法。

  • JDK8中新增的静态方法

    • 接口中的静态方法类似于类中的静态方法,必须用static修饰。静态方法默认都有public修饰。

    • 调用方式:必须使用接口名本身来调用。

内部类

内部类

内部类是定义在类里面的类,也是类的三大成分之一(成员变量、成员方法、内部类)之一。

  • 语法

    • 使用外部类的对象创建内部类的对象

    • 外部类的对象名.内部类 对象名 = 外部类的对象名.new 内部类();

  • 内部类的使用场景

    • 在企业开发中,几乎不会自己编写内部类。

    • 内部类往往都是出现了在JDK或者其他类库的源码中,且往往都是直接在当前类的其他方法中创建内部类的对象

静态内部类

成员变量、成员方法可以用static修饰,同样,内部类也可以用static修饰,被static修饰的内部类叫做静态内部类

  • 语法

    • 使用外部类.静态内部类创建静态内部类的对象

    • 外部类.内部类 对象名 = new外部类名.内部类名();

  • 静态内部类的使用场景

    • 如果要在其他类中更方便地使用该内部类的对象,就将内部类声明为静态内部类。

匿名内部类

匿名内部类是没有名字的内部类,直接定义在方法内部。

作用是将编写接口实现类和创建实现类的对象合并到一起完成,用来快速创建抽象类或接口实现类的对象,简化开发。

  • 语法

    • new 父类名/接口名(){

      重写父类或者接口名的抽象方法

      }

匿名内部类的使用场景

  1. 创建自己定义的接口实现类对象。(很少用)

  2. 调用JDK中的方法或者第三方依赖中的方法时,方法需要接收一个接口类型的对象作为参数。(经常出现)

  • Arrays工具类中的方法

    方法签名作用
    public static String toString(Object[] a)返回任意类型(包括基本数据类型)数组内容的字符串表示
    public static void sort(Object[] a)对任意类型(包括基本数据类型)的数组进行排序
    public static void setAll(int[] array, IntUnaryOperator generator)按照指定的规则修改数组中的每一个元素
    public static <T> void sort(T[] a, Comparator<? super T> c)制定比较规则对任意类型的数组进行排序
  1. 案例:Arrays类的setAll方法

    方法签名作用
    public static void setAll(int[] array, IntUnaryOperator generator)按照指定的规则修改数组中的每一个元素
    • IntUnaryOperator

      • setAll方法第二个参数需接收一个IntUnaryOperator接口类型的对象

      • 接口是不能直接创建对象的,使用匿名内部类快速得到一个接口类型的对象

  2. 案例:Arrays类的sort方法对于Comparator比较器的支持

    方法签名作用
    public static void sort(Object[] a)对任意类型的数组进行排序
    public static <T>void sort(T[] a, Comparator<? super T> c)指定比较规则对任意类型的数组进行排序
    • Compatarator

      • compatarator方法的比较约定:

        • return 比较者对象的值 - 被比较者对象的值:升序

        • return 被比较者对象的值 - 比较者对象的值:降序

在调用方法的时候,如果方法需要接收接口或者抽象类类型的参数,可以直接new接口/抽象类名,然后重写接口或者抽象类中的抽象方法。

Lambda表达式

认识Lambad表达式

Lambda表达式是从JDK8开始新增的一种语法形式,用来简化函数式接口的匿名内部类的代码写法

函数式接口(Functional Interface)

  • 函数式接口指的是有且只有一个抽象方法的接口,接口本身就是为了一个完成一个功能(Function)。

  • 函数式接口上方通常会加上一个@FunctionInterface注解,有这个注解的接口一定是函数式接口。

Lambda表达式的语法

接口名 对象名 = (被重写方法的形参列表)->{

被重写方法的方法体代码;

};

public class Test {
    public static void main(String[] args) {
        Runnable r = () -> {
            System.out.println("狗跑得快"); 
        };
        r.run
    }
}

使用Lambda表达式简化Arrays工具类中的setAll方法的匿名内部类

public class Test {
    public static void main(String[] args) {
        int[] arr = new int[]{80,90,100};
        Arrays.set(arr,(int i)->){
            return arr[i]*2;
        });
        System.out.println(Arrays.toString(arr));
    }
}

使用Lambda表达式简化Comparator接口的匿名内部类

public class Test{
    public static void main(String[] args){
        Student[] students = new Student[3];
        students[0] = new Student("玛卡巴卡",16,170.1);
        students[1] = new Student("唔西迪西",13,160.1);
        students[2] = new Student("依古比古",18,150.1);

        Arrays.sort(students,(Student o1,Student o2)->){
            return o1.getAge() - o2.getAge();
        });
        System.out.println(Arrays.toString(students));
    }
}

Lambda表达式的省略规则

  • 参数类型可以省略不写。

  • 如果只有一个参数,参数类型可以省略,同时()也可以省略。

  • 如果Lambada表达式的方法体只有一行代码,可以省略大括号不写,同时要省略分号;此时,如果这行代码是return语句,还必须省略return关键字。

方法引用

  • 方法引用是Lambda表达式的最简化写法,本质上还是一个Lambda表达式。方法引用的操作符是双冒号::

方法引用的使用场景

  • 如果Lambda表达式箭头右边的业务逻辑已经有方法实现了,那么可以通过方法引用的形式引用已有的方法,以达到简化Lambda表达式的目的。

方法引用的形式

  • 类的静态方法引用

    类名::静态方法名

    • 使用场景

      • 如果Lambda表达式箭头右边只是调用了一个静态方法,并且箭头前后参数一致,就可以使用类的静态引用简化了;

        Arrays.sort(students,CompareDate::compareByAge)
  • 对象的实例方法引用

    对象名::实例方法名

    • 使用场景

      • Lambda表达式的箭头后边只是调用了一个实例方法,并且箭头前后参数形式一致,就可以使用对象的实例方法引用简化了。

        CompareDate cd = new CompareDate();
        Arrays.sort(students,cd::compareByAgeDesc);
  • 类的实例方法引用

    类名 ::实例方法名

    • 使用场景

      • Lambda表达式的箭头后边只是调用了一个实例方法,并且箭头前面参数列表中的第一个参数是方法的调用者,后面所有的参数都是该实例方法的入参,就可以使用类的实例方法引用简化了。

        String[] names = {"Jack","Mory","Jarry"};
        Arrays.sort(names,String::compareTolgnoreCase);
  • 构造方法引用

    类名::new

    • 使用场景

      Lambda表达式的箭头右边只是在创建对象,并且箭头前后参数情况一致,就可以使用构造方法引用简化了。

      CarInter1 inter1 = Car::new;
      CarInter2 inter2 = Car::new;
  • 数组构造方法引用

    数据类型[]::new

    • 使用场景

      Lambda表达式的箭头右边只是在创建数组对象,并且箭头前后参数情况一致,就可以使用数组构造方法引用简化了。

      IntFunction<int[]> arrayCreator = int[]::new;
      int[] arr = arrayCreator.apply(5);
    • 数组构造方法引用本质上和构造方法引用是一样的,把数据类型[]看成是一种数据类型即可

异常

异常概述和异常体系

  • 异常

    • 异常代表程序出现的问题

    • 异常一旦出现,如果没有提前处理,程序就会退出JVM虚拟机而终止

    • 研究异常并且提前处理异常,体现的是程序的安全性和健壮性

  • 异常体系

    • Throwable

      • Error

        由JVM抛出来的系统级别的严重错误,程序员无法直接干预

      • Exception

        Java.lang包下定义的异常类,表示程序出现的可以处理的问题

        • FileNotFoundException

        • ParseException

        • RuntimeException

          运行时异常:直接或间接继承自RuntimeException的异常类,特点是编译阶段不报错,运行时可能报错

          编译时异常:没有继承自RuntimeException的异常类,特点是编译阶段就报错,必须提前处理,否则程序无法通过编译,也称为受检查的异常(CheckedException)。

          • ArithmeticException

          • NullPointerException

          • IndexOutOfBoundsException

            • ArrayIndexOutOfBoundsException

            • StringIndexOutOfBoundsException

          • 许多异常类…

        • 许多异常类…

编译时异常的处理方式

  • 编译时异常的处理方式

    • Java的异常处理模型基于一下三个操作:抛出异常(throw),声明异常(throws)捕获异常(try-catch)

    • 三者关系如下

    • throw:在方法内部,作用是抛出一个异常

      throws:在方法声明(方法签名)之上,作用是声明一个异常

注:一旦在方法上声明了异常,方法调用处一定要处理对应的异常

自定义编译时异常

  • 自定义异常

    Java无法为这个世界上全部的问题都提供对应的异常类,如果企业想通过异常的形式管理自己的业务问题,就需要自己定义异常类了。

  • 自定义异常的分类

    1. 定义一个异常类继承Exception

    2. 在异常类中重写无参数构造器和只接收异常信息作为参数的构造器

    3. 在需要使用异常的方法内部通过throw new 异常类(xxx)的方法创建异常对象并抛出

    4. 在抛出异常的方法签名上使用throws声明定义异常。

运行时异常的处理方式

  • 运行时异常的处理方式

    • 运行时异常在编译阶段不会报错,是运行时才可能出错的,所以编译阶段不处理也可以。

    • 按照规范还是建议处理:

      • 方式一:在最外层调用处集中处理。(不推荐)

      • 方式二:提前预判,使用if分支排除可能出现的异常。(推荐)

自定义运行时异常

  1. 定义一个异常类继承RuntimeException

  2. 在异常类中重写无参数构造器和只接收异常信息作为参数的构造器

  3. 在需要使用异常的方法内部通过thorw new 异常类(xxx)的方式创建异常对象并抛出

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值