基本介绍 面向对象编程有三大特征:封装、继承和多态。
7.1 封装
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
封装的实现步骤(三步)
1)将属性进行私有化private【不能直接修改属性】
2)提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型参数名)//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
3)提供一个公共的(public)get方法,用于获取属性的值
public数据类型getXxx({//权限判断,Xxx某个属性
return xx;}
7.2 常量
含义:
就是固定的,不能改变的量。只能用,不能修改, 如final String gender ="Male";
注意:
- final修饰的基本数据类型,值不能改变,修饰引用数据类型,不能重新new对象
- final和static关键字连用,叫静态常量,属性命名必须是大写 ,下划线分隔,如 static final String FEMALE_SEX= "Female";
- static 修饰类中属性,叫静态属性或类属性、类成员,类名.属性名(推荐)或对象名.属性名
public static void main(String[] args) { Constant cons = new Constant(); System.out.println(cons.FEMALE_SEX); //对象名.属性 System.out.println(Constant.FEMALE_SEX);//类名.属性 推荐使用 }
7.3 继承
7.3.1继承基本介绍
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来 声明继承父类即可。
7.3.2继承的基本语法
class子类extends 父类{
1)子类就会自动拥有父类定义的属性和方法
2)父类又叫超类,基类。
3)子类又叫派生类。
}
7.3.3继承的深入讨论/细节问题
1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2)子类必须调用父类的构造器, 完成父类的初始化
3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译 不会通过 。
4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7) java 所有类都是 Object 类的子类, Object 是所有类的基类.
8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
9) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
如何让 A 类继承 B 类和 C 类? //【A 继承 B, B 继承 C】
10) 不能滥用继承,子类和父类之间必须满足 is-a (什么是什么)的逻辑关系
7.4 super 关键字
7.4.1基本介绍
super 代表父类的引用,用于访问父类的属性、方法、构造器 。
7.4.2 基本语法
- 访问父类的属性,但不能访问父类的private属性[案例]super.属性名;
- 访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
- 访问父类的构造器(这点前面用过):
super(参数列表);只能放在构造器的第一句,只能出现一句!
7.4.3调用父类的构造器的好处
- 分工明确,父类属性由父类初始化,子类的属性由子类初始化
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super.this、直接访问是一样的效果!
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。当然也需要遵守访问权限的相关规则
7.4.4 super 和 this 的区别
7.5 方法重写/覆盖(override)
7.5.1 基本介绍
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
7.5.2注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件:
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表方法名称完全一样。
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类,比如父类返回类型是 Object,子类方法返回类型是String。
7.5.3 重载 和 重写 的区别
7.6 抽象类和抽象方法
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
7.6.1抽象类的介绍
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类
访问修饰符abstract类名{
}
- 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型方法名 (参数列表); //没有方法体
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
7.6.2抽象类使用的注意事项和细节
1) 抽象类不能被实例化。
2) 抽象类不一定要包含abstract方法。也就是说.抽象类可以没有abstract方法。
3) 一旦类包含了abstract方法,则这个类必须声明为abstract 。
4) abstract只能修饰类和方法,不能修饰属性和其它的。
5) 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法.构造器、静态属性等等[举例]
6) 抽象方法不能有主体,即不能实现。
7) 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
8 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
7.7 多态
7.7.1多态基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
7.7.2多态的具体体现
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的.
- 编译类型看定义时=号的左边,运行类型看=号的右边
7.7.3多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法: 父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限),
不能调用子类中特有成员;
最终运行效果看子类的具体实现!
多态向下转型
- 语法: 子类类型 引用名 = (子类类型)) 父类引用;
- 只能强转父类的引用,不能强转父类的对象。
- 要求父类的引用必须指向的是当前目标类型的对象。
- 当向下转型后,可以调用子类类型中所有的成员。
7.8 Object 类
== 和equals 的对比
== 是一个比较运算符
- ==: 既可以判断基本类型,又可以判断引用类型;
- ==: 如果判断基本类型,判断的是值是否相等。示例:int i=10;double d=10.0;
- ==: 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象;
- equals:是Object类中的方法,只能判断引用类型:
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
instanceof
instanceof 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
System.out.println( aa instanceof AA);
7.9 接口
7.9.1 基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
语法:
interface接口名{
//属性
//抽象方法
}
class类名 implements 接口{
自己属性;
自己方法;
必须实现的接口的抽象方法
}
小结:接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有,方法体【jdk7.0】。接口体现了程序设计的多态和高内聚低偶合的设计思想。
特别说明:Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中司以有方法的具体实现
7.9.2特征
- 接口中的属性默认是public static final型(静态常量)
- 接口中的方法通常是public abstract型抽象方法
- 接口中可以有具体方法,但要添加default关键字(jdk 1.8以后支持)
- 接口中可以有静态方法(jdk1.8以后)
- 接口中支持多继承
- 接口中没有构造方法,不能产生对象
- 类在继承同时,可以实现多个接口
-
- 类与类之间:单继承
- 类和接口之间:实现 多个接口
-
- 接口与接口之间:多继承
- 接口中的属性,只能是final的,而且是 public static final修饰符。比如:int a=1;实际上是 public static final int a=1;(必须初始化)
- 接口中属性的访问形式:接口名.属性名
7.9.3 接口和抽象类的区别
- 接口之间是多继承,抽象类是单继承
- 接口中属性是静态常量,而抽象类不需要
- 接口中没有构造方法,而抽象类有(但不能创建对象)
- 接口中的可以是抽象方法或default修饰的具体方法(jdk1.8新增),而抽象类中可以有任意类型的方法
7.10内部类
如果定义类在局部位置(方法中/代码块) : (1) 局部内部类 (2) 匿名内部类
定义在成员位置 : (1) 成员内部类 (2) 静态内部类
7.10.1基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成 【 类的五大成员是哪些? 属性、方法、构造器、代码块,内部类】
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.
语法:
class outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
7.10.2 内部类的分类
定义在外部类局部位置上(比如方法内):
1)局部内部类(有类名)
2)匿名内部类(没有类名,重点!!!!!!!!)
定义在外部类的成员位置上:
1)成员内部类(没用static修饰)
2)静态内部类(使用static修饰)
7.10.3 局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
- 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码块中。
- 局部内部类---访问---->外部类的成员[访问方式:直接访问]
- 外部类---访问---->局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
记住:(1) 局部内部类定义在方法中/代码块
(2) 作用域在方法体或者代码块中
(3) 本质仍然是一个类
- 外部其他类---不能访问----->局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
7.10.4 匿名内部类的使用
(1)本质是类 (2)内部类 (3)该类没有名字 (4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
- 匿名内部类的基本语法
new 类或接口(参数列表){
类体
}
- 匿名内部类的语法比较奇特,请往意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
- 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它的地位就是一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类---访问---->外部类成员[访问方式: 直接访问]
- 外部其他类---不能访问--->匿名内部类(因为匿名内部类地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则m如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
7.10.5 成员内部类的使用
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。
1.可以直接访问外部类的所有成员,包含私有的
2.可以添加任意访问修饰符(public.protected、默认、private),因为它的地位就是一个成员。
3.作用域和外部类的其他成员一样,为整个类体,在外部类的成员方法中创建成员内部类对象,再调用方法.
4.成员内部类---访问---->外部类成员(比如:属性) 访问方式: 直接访问
5.外部类---访问------>成员内部类 访问方式:创建对象,再访问
6.外部其他类---访问---->成员内部类
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问.
7.11枚举
枚举是一组常量的集合。
枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
7.11.1 自定义枚举
- 不需要提供setXxx方法,因为枚举对象值通常为只读.
- 对枚举对象/属性使用final+ static共同修饰,实现底层优化.
- 枚举对象名通常使用全部大写,常量的命名规范.
- 枚举对象根据需要,也可以有多个属性//Enumeration02.java
7.11.2 enum 关键字实现枚举注意事项
1) 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类[如何证明],老师使用 javap 工 具来演示
2) 传统的 public static final Season2 SPRING = new Season2("春天", "温暖"); 简化成 SPRING("春天", "温暖"), 这里必须知道,它调用的是哪个构造器.
3) 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
4) 当有多个枚举对象时,使用,间隔,最后有一个分号结尾
5) 枚举对象必须放在枚举类的行首.
7.12 克隆对象
步骤:
- 重写Object对象中clone()方法
- 实现Cloneable标识型接口
7.13 异常-Exception
7.13.1 基本概念
Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两大类
1)Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如: StackOverflowError[栈溢出]和OOM(out ofmemory).Error是严重错误,程序会崩溃。
2)Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception 分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。
7.13.2 异常体系图
异常体系图的小结 :
- 异常分为两大类,运行时异常和编译时异常.
- 运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
- 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响
- 编详时异常,是编译器要求必须处置的异常。
7.13.3 常见的运行时异常
常见的运行时异常包括
1) NullPointerException 空指针异常
2) ArithmeticException 数学运算异常
3) ArrayIndexOutOfBoundsException 数组下标越界异常
4) ClassCastException 类型转换异常
5) NumberFormatException 数字格式不正确异常
7.13.4 编译异常
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
常见的编译异常:
sQLException //操作数据库时,查询表可能发生异常
IOException //操作文件时,发生的异常
FileNotFoundException //当操作一个不存在的文件时,发生异常
ClassNotFoundException //加载类,而该类不存在时,异常
EOFException //操作文件,到文件未尾,发生异常
lllegalArguementException //参数异常
7.13.5 try-catch异常处理
try-catch方式处理异常说明
1)Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个try...catch块。
2)基本语法
try {
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch(异常){
//对异常的处理
}
//如果没有finally,语法是可以通过
7.13.6 try-catch方式处理异常
-注意事项
1)如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块.
2)如果异常没有发生,则顺序执行try的代码块,不会进入到catch.
3)如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码- finally {}
4)可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,
(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch。
5)可以进行try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑
7.13.7 throws 异常处理
一、基本介绍
1) 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
2) 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
二、注意事项和使用细节
1) 对于编译异常,程序中必须处理,比如try-catch或者 throws2)对于运行时异常,程序中如果没有处理,默认就是throws的方式处理。
3)子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型。
4)在throws过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws。
7.13.8自定义异常
一、基本概念
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描
述处理,这个时候可以首己设计异常类,用于描述该错误信息。
二、自定义异常的步骤
1)定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException
2)如果继承Exception,属于编译异常
3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
三、 throw 和 throws 的区别