面向对象【Java语言的核心机制,最重要的内容,Java语言特色】
[内容时间:2021.08.09]
面向过程与面向对象的区别
面向过程
主要关注点是实现的具体过程,因果关系【集成显卡的开发思路】
优点
对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低。
缺点
采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间的“耦合度”非常高,只要其中一环出问题,整个系统受到影响,导致最终软件的“扩展力”差。另外,由于没有独立体的概念,所以无法达到组件复用 !
面向对象
主要关注对象【独立体】能完成哪些功能。【独立显卡的开发思路】
优点
耦合度低,扩展力强,更容易解决现实世界当中更复杂的业务逻辑。组件复用性强 !
缺点
前期投入成本较高,需要进行独立体的抽取,大量的分析与设计。
面向对象的三大特征
封装
封装的好处
1、对于这个事物来说,看不到这个事物的复杂的一面,对外提供简单的操作入口。对于使用者来说不需要知道内部复杂的实现原理,只需要会简单的操作就行。
2、封装之后才会形成真正的“对象”,真正的“独立体”。
3、封装就意味着以后的程序可以重复使用,并且这个事物适应性比较强,任何场合都适用
4、封装之后,对于事物本身,提高了安全性。【安全级别高】
封装的步骤
1、所有属性私有化,用private关键字修饰,修饰的所有数据只能在本类中访问。
2、对外提供简单的操作入口
对外提供两个公开的方法,分别是set方法和get方法
3、set方法的命名规范:
/*public void set+属性名首字母大写(形参){ }*/ public void setAge(int a){ age = a ; //例如这样 }4、get方法命名规范
public int getAge(){ return age; }
继承
关于java语言当中的继承
//语法格式:
【修饰符列表】class 类名 extends 父类名{
//类体
}
1、继承的“基本”作用是:代码复用。但继承最“重要”的作用是:有了继承才有了以后的“方法覆盖”和“多态机制”。
2、Java语言当中的继承只支持单继承,一个类不能同时继承很多类。【在C++中支持多继承】
3、关于继承的一些术语:
B类继承A类,其中:
A类成为:父类,基类,超类,superclass
B类称为:子类,派生类,subclass
4、在Java语言当中子类继承父类都继承哪些数据呢?
私有的不支持继承
构造方法不支持继承
其他数据都可以继承
5、虽然Java只支持单继承,但是一个类也可以间接继承其他类,例如:
C extends B{ }
B extends A{ }
A extends T{ }
那么C类直接继承B类,但是C类间接继承A类T类。
6、Java中假设一个类没有显示的继承任何类,该类默认继承JavaSE类库当中提供的java.lang.Object类。
Java语言中任何一个类都有Object类的特征
多态
单例模式(现在的水平不够 P149,先不讲 )
向下转型需要留意一下:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// instanceof语法格式:
// (引用 instanceof 数据类型名)
// 运算结果为布尔类型
Animal a1 = new Cat();
// 要访问子类特有对象时,要确定a1确实是个猫,那才能做向下转型,要不然会抛出异常,但不会报错
if(a1 instanceof Cat){
Cat c1 = (Cat)a1;
}
构造方法
关于java类的构造方法
1、构造方法又称为构造函数、构造器、Constructor
2、构造方法的语法结构
[修饰符列表] 构造方法名(形参列表){ }
3、回顾普通方法的语法结构
[修饰符列表] 返回值类型 方法名(形参列表){ }
4、对于构造方法来说,“返回值类型不需要指定”,而且也不能写void,写了就是普通方法了
5、构造方法的方法名必须与类名保持一致
6、构造方法的作用是:通过构造方法的调用,可以创建对象,并给实例变量赋值
7、怎样调用构造方法?
普通方法:有static时,类名.方法名(实参) ; 无static时,引用.方法名(实参)
构造方法:new 构造方法名(实参)
8、构造方法执行后有返回值吗?
每一个构造方法实际上执行结束后都有返回值,但是return这个语句不用写,因为返回值类型就是类本身,所以返回值类型也不用写
9、注释和取消注释:ctrl + /
10、当一个类中没有定义任何构造方法,系统默认给类提供一个无参数构造方法,称之为缺省构造器。
11、当一个类显示的构造方法定义出来了,那么系统则不再默认为这个类提供缺省构造器,建议开发中手动为当前提供无参数构造方法,因为无参数构造方法太常用了
12、构造方法支持重载机制,在一个类当中编写多个构造方法,这个多个构造方法显然已经形成方法重载机制
类和对象的概念
类的定义
JVM的内存划分
几种方法的用法
静态方法:
public static void 方法名(){ } 这个是普通的类的方法 // 用法是类名.方法名
实例方法:
public void 方法名(){ } 这个是可以访问类的private变量 方法入口 // 用法是引用.方法名
构造方法:
public 方法名(){ } 这个是构造方法 // 用法是new
this关键字
this能出现在实例方法和构造方法中
this() 在构造方法出现时只能出现在的第一行(它的上头不能有语句)作用是通过当前的构造方法去调用“本类”中的其他构造方法,目的是:代码复用
static关键字
静态变量
什么时候定义静态变量呢?
所有对象都有这个属性,而且所有对象的这个属性的值都是一样的,建议定义为静态变量,节省内存的开销
静态变量在类加载的时候初始化,内存在方法区中开辟。访问的时候不需要创建对象,直接使用 “ 类名.静态变量名 ” 的方式访问
public class Chinese{
String id;
String name;
//国籍,所有对象的国籍都一样,这属于类级别,可以做变量前加static关键字修饰
//静态变量在类加载的时候初始化,不需要创建对象,内存就开辟了
static String country = "中国";
}
静态方法
1、方法什么时候定义为静态的?
方法描述的是动作,当所有对象执行这个动作的时候,最终产生的影响都是一样的,那就不属于某一个对象了,可以提升为类级别的动作,模板级别的动作
2、大多数方法都定义为实例方法,因为一般一个行为一个动作在发生的时候都需要对象的参与。【但也有例外】。大多数“ 工具类 ”中的方法都是静态方法,因为工具就是为了方便编程,方便调用。自然不用new是最好的
静态代码块
//语法格式:
static{
//Java语句;
}
1、静态代码块在类加载的时候执行,并且只执行一次
2、静态代码块在一个类中可以编写多个,遵循自上而下依次执行
3、静态代码块的作用是?怎么用?用在哪?什么时候用?
和需求有关,例如项目中要求在类加载的时刻 / 时机执行代码完成日志记录,那么这段记录日志的代码就可以编写到静态代码块当中,完成日志记录
静态代码块是Java为程序员准备的一个特殊的时刻,这个特殊的时刻被称之为类加载时刻。若希望在此时刻执行一段特殊的程序,这段程序可以直接放到静态代码块当中。
4、通常在静态代码块当中完成预备工作,先完成数据的准备工具,例如:初始化连接池,解析XML配置文件……
【拓展】实例(代码块/语句块)
(类似static代码块的用法)【了解内容,使用非常少】
1、实例代码块可以编写多个,自上而下执行
2、实例代码块在构造方法执行之前执行,构造方法执行一次,实例代码块对应执行一次
3、实例代码块也是Java语言为程序员准备的一个特殊的时机,称之为:对象初始化时机
//语法为
public class Xx{
{
//java语句;
}
}
4、想有输出,那在主方法里面new吧
方法覆盖
(又称之为方法重写,Override)
关于Java语言中的方法覆盖
1、什么时候使用方法覆盖?
当父类当中的方法已经无法满足当前子类的业务需求。子类有必要将父类中继承过来的方法进行重新编写,这就叫做方法覆盖
2、什么条件满足之后会发生方法覆盖呢?
①方法覆盖发生在具有继承的父子类之间
②方法覆盖的时候:返回值类型相同,方法名相同,形参列表相同
③方法覆盖的时候:访问权限不能更低,可以更高
④方法覆盖的时候:抛出异常不能更多,可以更少。【以后讲,讲完异常之后才能解释】
温馨提示:建议方法覆盖的时候尽量复制粘贴,自己编写容易出错,导致没有产生覆盖
3、注意事项
私有方法不能继承,所以不能覆盖
构造方法不能继承,所以不能覆盖
静态方法不存在覆盖
覆盖只针对方法,不涉及属性
final关键字
-
final 修饰的类无法被继承
-
final 修饰的方法无法被覆盖
-
final 修饰的变量只能赋一次值
-
final 修饰的引用一旦指向某个对象,则不能再重新指向其他对象,但该引用指向的对象内部的数据是可以修改的
-
final 修饰的实例变量必须手动初始化,不能采用系统默认值 NULL
-
final 修饰的实例变量一般和 static 联合使用,成为常量
public static final double PI = 3.1415926 ;
抽象类
-
抽象类由 abstract 修饰在 class 前面
-
抽象类的作用是降低接口在实现类与接口之间的实现难度
-
使用规则
抽象类中可以声明抽象方法,也可以写具体方法
抽象类声明抽象方法时,子类必须重写该抽象方法
抽象类实现接口时,不需要对接口的方法进行重写
抽象类由构造方法,但是不能使用
接口
接口是一种特殊的类文件(.class文件)
接口是一种引用数据类型
作用是
- 制定规则
- 降低耦合度
使用规则
- 接口中的属性,默认都是静态常量属性(常量修饰符列表一般采用 public static final ,但接口里面的 public static final 可以省略不写)
- 接口中的方法都是抽象方法,如果需要定义具体方法的实现,此时方法需要使用default修饰
- 接口中的抽象方法定义时:public abstract 修饰符可以省略不写
- 接口中的方法访问权限不能是private
- 接口与接口之间可以实现多继承,但是接口之间不能相互实现
- 接口中不存在构造方法
关于接口中的 static 方法( 静态方法 )
关于接口中的default方法( 默认方法 )
在java8以后,接口中可以添加使用default或者static修饰的方法,在这里我们只讨论default方法,default修饰方法只能在接口中使用,在接口中被default标记的方法为普通方法,可以直接写方法体。
一、概念
- 接口提供一个默认实现的方法,并且不强制实现类重写此方法
- 默认方法使用default关键字来修饰
二、引入背景
- 当一个接口添加新方法时,需要所有的实现类都重写新方法,影响到了已有的实现类,可能导致应用崩溃
- 默认方法可以不强制重写,也不会影响到已有的实现类
- 例如Iterable接口的foreach方法,就是一个默认方法,参数是一个Consumer对象
三、使用
- 当一个实现类实现了多个接口,多个接口里都有相同的默认方法时,实现类必须重写该默认方法,否则编译错误
a. 实现类自己重写逻辑
b. 实现类使用super关键字指定使用哪个接口的默认方法
- 接口静态方法
接口中支持定义静态方法,将默认方法的default关键字换成static即可
- 继承类可以直接使用接口中的static方法,也可以创建对象后使用接口中的default方法
======================
实现类会继承接口中的default方法。如果接口A中有default方法:
public interface A {
public default void a(){
System.out.println("这是A");
}
}
Test类实现接口A:
public class Test implements A{
}
那么Test类将会继承接口A中的a方法:
public class Test2 {
public static void main(String[] args) {
Test t = new Test();
t.a();
}
}
2.如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法
为什么要重写呢?是因为,类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方法。
接口A:
public interface A {
public default void a(){
System.out.println("这是A");
}
}
接口B:
public interface B {
public default void a(){
System.out.println("这是B");
}
}
Test类:
public class Text implement A,B{ //这行会提示报错,因为没重写 a()方法
}
3.如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子类会继承父类的b方法而不是继承接口中的b方法
接口A:
public interface A {
public default void b(){
System.out.println("AAA");
}
}
类C:
public class C {
public void b(){
System.out.println("CCC");
}
}
子类 Text 类:
public class Test extends C implements A{
}
测试类:
public class Text2{
public static void main(String[] args){
Text t = new Text();
t.b();
}
}
说明子类继承的b方法为父类C中的b方法,不是接口中的default b(){}方法。
Object类的常用方法
toString
如果输出类型是引用类型的话,系统自动调用toString方法
equals
内部类
匿名内部类
静态内部类
实例内部类
局部内部类,很少用,几乎不用
但匿名内部类又是局部内部类的一种
使用内部类编写的代码可读性很差,能不用尽量不用