面向对象的编程
面向对象
- 面向过程: 步骤清晰, 适合处理简单问题(线性思维)
- 面向对象: 分类问题, 再对这些分类进行单独思考; 适合复杂问题和多人合作
宏观: 面向对象思想: 微观: 面向过程的思想
面向对象的编程(Object-Oriented Programing, OOP)
- 本质:以类的方式组织代码, 以对象的方式封装数据
- 核心:抽象
- 特性:
- 封装
- 继承
- 多态
- 从认识的角度考虑: 先有对象后有类:(对象:具体事物; 类: 对象的抽象)
- 从代码运行的角度: 先有类后有对象: 类是对象的模板
方法回顾
方法的定义
修饰符
public private static ...
返回类型
break\return的区别
break用于switch
方法名
见名知意, 首字母小写与驼峰原则
参数列表
参数类型, 参数名 …
异常抛出
先后文
方法调用
静态方法(和类一起加载)
static 直接调用
非静态方法(类实例化才存在)
对象实例化(new)
形参和实参
值传递\引用传递
int a = 1 ;
//Demo 为当前类名
Demo.change(a);//a的值不改变(值传递)
public static void change(int a){
a = 10 ;
}
public class Draft {
String name;
public void change(Draft obj){
obj.name = "Andrew";
}
public static void main(String[] args) {
int ID = 2020;
Draft obj = new Draft();
System.out.println(obj.name);
new Draft().change(obj);//传递的为引用类型
System.out.println(obj.name);
}
}
this关键字
类与对象的关系
- 类是抽象的数据类型, 是对某一类事物整体的描述\定义, 但不代表某一具体事物;
- 动物\植物…
- Person类\Pet类\Car类
- 对象是抽象概念的具体实例
- 例如: 张三是人的实例
- 能够体现出特点, 展现出功能的是具体实例, 而不是一个抽象概念
创建与初始化对象
- 使用new关键字创建
- 使用new创建时, 除分配内存空间外, 还会给创建好的对象进行默认的初始化及对类中构造器的调用
- 类中构造器也称构造方法,是在进行创建对象时必须要调用的;
- 构造器有以下两个特点:
- 必须和类的名字****相同
- 没有返回类型, 但不写void
- 构造器(快捷键:
alt+insert
)
- 使用new关键字本质实在调用构造器
- 构造器用来初始化值
- 不写构造函数默认有无参构造
- 构造方法可以有多个(类似方法 的重载)
- 一旦定义了有参构造, 无参构造就必须显示定义(方法重载)
- 有参: 便于赋初值
- 无参: 初值为默认值
package class_object;
//一个类包含属性及方法
public class Person {
//属性
String name;
int age;
//方法
public static void say(){
System.out.println("Hello,world!");
}
//无参构造
public Person() {
}
//有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
package class_object;
public class Application {
public static void main(String[] args) {
//有参构造
Person James = new Person("James",32);
//无参构造
Person Sam = new Person();
System.out.println(James.name+'\t'+James.age);
System.out.println(Sam.name+'\t'+Sam.age);
}
}
- 内存分析
package class_object;
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
Pet cat = new Pet();
dog.name = "旺财";
dog.age = 3;
System.out.println(dog.name+'\t'+dog.age+'岁');
System.out.println(cat.name+'\t'+cat.age+'岁');
}
}
package class_object;
public class Pet {
public String name;
public int age;
public void shout(){
System.out.println("叫了一声");
}
}
小结
- 类与对象: 类是模板(抽象) , 对象是类的具体实例
- 方法: 定义\调用
- 对应的引用
基本类型(8)\引用类型
对象是通过引用来操作的: 栈–>堆(地址) - 属性: 字段field成员变量
默认初始化:
- 数字 0\0.0
- char u0000
- Boolean false
- 引用 null
修饰符+属性类型+属性名 = 属性值
- 对象的创建和使用
- 必须使用 new (有构造器)
- 对象的属性
- 对象的方法
- 类
静态的属性: 属性
动态的行为: 方法
封装
- 程序设计追求高内聚,低耦合:
- 高内聚: 类的内部数据操作细节自己完成, 不允许外部干涉
- 低耦合: 尽量暴露少量方法给外部使用
- 封装(数据隐藏)
- 通常, 应禁止访问一个对象中数据的实际表示, 而应通过操作接口来访问
- 属性私有, get\set
- 属性私有: private(与public)相反
- 提供可操作属性的方法(public)
- get :获得这个数据
- set :给这个数据设置值(可增加 if 判断是否合法)
alt + insert自动生成getter\setter
package class_object;
public class GetterAndSetter {
public static class Student{
//定义私有属性
private String name ;
private int age;
public String getName(){
return this.name;
}
//定义公开方法
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
}
}
package class_object;
import class_object.GetterAndSetter.Student;//引用Student类
public class Application {
public static void main(String[] args) {
Student stu = new Student();
//通过setter给stu赋值
stu.setName("Andrew");
stu.setAge(20);
//stu.后不能接Student类的属性name和age(private)
System.out.println(stu.getName() + ' ' + stu.getAge());
}
}
- 封装的意义:
- 提高程序安全性, 保护数据
- 隐藏代码实现细节
- 同一接口
- 系统可维护性增加
继承
- 继承的本质是对某一批类的抽象, 从而实现对现实世界更好的建模(宏观把握)
- extends:子类是父类的扩展
- Java中只有 单继承 ,无多继承; 可以多重继承(C extends B;B extends A就会出现继承体系)
使用继承体系时, 先查看体系中的顶层类, 了解基本功能; 创建体系中最子类的对象, 完成功能 - 继承是类与类的一种关系(此外还有:以依赖\组合\聚合)
继承的关系的两个类, 一个为子类(派生类), 一个为父类(基类), 自类继承父类, 使用关键字extends来表示
子类继承父类所有方法\属性值 (前提: 父类非私有 private )
子类和父类之间, 从意义上讲应该有"is a"的关系 - object类
- java中所有类都直接或间接继承object类
- super
- super调用父类的构造方法, 必须写在构造方法的第一个
子类的构造器默认调用父类的无参构造(super(); ,所以父类改为有参构造后要及时手动添加其无参构造) - super只能出现在子类的方法\构造方法中
- super\this 不能同时调用构造方法(二者都要求为构造函数第一条语句)
- 与this 的异同
- 代表的对象不同:
- super: 代表父类对象的引用
- this: 本身调用这个对象
- 前提不同:
- super: 只有继承时可用
- this: 无继承也可用
- 构造方法不同:
- super():父类的构造
- this(): 本类的构造
- 方法重写(静态方法)
重写的是方法, 和属性无关
- 注意点: 需要有继承关系(子类重写父类)
- 方法名相同
- 参数列表相同
- 修饰符范围可以扩大, 不能缩小
public>protected>default>private
- 抛出异常范围可以缩小, 但不能扩大
ClassNotFoundException<Exception
- 为什么需要重写:
- 子类不需要\不满足父类的功能
-
alt+insert
:重写 (override) 快捷键
多态
含义
- 同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的, 但可以指向对象的引用类型(父类\有关系的类)有很多种
注意点:
- 多态是方法的多态, 属性无多态
- 父类及子类要有联系, 否则类型转换异常!
ClassCastException!
- 存在条件:
- 继承关系
- 方法重写
- 有些方法无法被重写:
//1.static方法, 属于类不属于实例
//2.final修饰, 属于常量
//3.private方法(私有)
- 父类引用指向子类
Father f1 = new Son();
- 注意点:
- 一个对象的实际类型是确定的, 但其指向的引用类型是不确定的;
子类能调用自己和父类的方法(继承), 父类可以指向子类, 但无法调用子类独有的方法;
instanceof
判断两个类有无父子关系
instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false。
注意事项
- 先有继承
- 用于引用类型
- null与任何引用类型进行instanceof比较结果均为false(null 没有引用任何对象)
小结
- 声明类型与比较的类型不相干, 编译不通过
- instanceof 右边不是左边实际类型的父类, 结果为false
类型转换
类似基本类型转换的高低规则
- 子类转父类( 低转高 )不需要强制类型转换
Person a = new Student();
- 父类转子类( 高转低 )要强制类型转换
Person obj = new Student();
//此时是父类引用子类对象, 不能使用子类独有的方法
((Student)obj).method();//(Student) 将这个对象强制转换位Student类型
static
修饰符, 表示静态(属性/方法)
属性:
public class Draft {
static int a;//静态变量 同类加载,全局可用 (多线程)
int b;//非静态的变量
public static void main(String[] args) {
System.out.println(Draft.a);//类变量可直接访问(被类中所有实例共享)
Draft obj = new Draft();//创建对象obj
System.out.println(obj.b);//非静态通过对象访问
}
}
方法:
public class Draft {
public static void staticMethod(){};//静态方法
public void method1(){};//非静态方法
public void method2(){
Draft.staticMethod();//非静态方法可直接访问静态方法
};//非静态方法
public static void main(String[] args) {
Draft.staticMethod();//静态方法可直接访问静态方法
staticMethod();//已经在类Draft中, 可省略类名
}
}
非静态方法无法引用
在非static中可以
静态代码块
public class Draft {
{
System.out.println("匿名代码块");//第二执行
}
static {
System.out.println("静态代码块");//最先执行
}
public Draft(){
System.out.println("构造方法");//最后执行
}
public static void main(String[] args) {
Draft obj = new Draft();
}
}
运行结果:
静态导入包
非静态导入:
import java.lang.Math;
public class Draft {
public static void main(String[] args) {
System.out.println(Math.random());
}
}
静态导入:
import static java.lang.Math.random;//加上static ,且直接导入方法random
public class Draft {
public static void main(String[] args) {
System.out.println(random());//可以省略 Math.
}
}
抽象类
- 关键字:
abstract
(不能修饰属性)
如声明抽象类:public abstract class Person
- 抽象类中可声明抽象方法:
public abstract doSomething();
注意点:
- 抽象类不能被new实例化(只能靠子类实现)
- 抽象类才有抽象方法, 抽象类也可以有非抽象的方法
- 抽象类的子类必须实现父类的抽象方法, 除非该子类也是抽象类(甩锅)
接口
对对象的抽象
实现伪多继承
- 接口的关键字: interface(接口)
public interface UserService{}
- 类的关键字:
implement
(实现)Impl 结尾
public class UserServiceImpl implements UserService{}
实现接口的类, 就要重写接口的方法 - 接口的作用
- 是一种约束
- 定义方法, 对同一接口可以有多种User的实现
- 默认的方法(default, 即不写修饰符):
public abstract
- 默认的常量 :
public static final
- 接口无法被实例化(无构造方法)
implements
可以实现多个接口(逗号分隔), 该类必须重写所有接口的方法
内部类
- 分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
package com.draft;
//对于Inner而言, Outer是外部类
public class Outer {
private String a = "这是外部私有属性";//私有的外部属性
private void print(){
System.out.println("这是私有外部方法");
}
//创建内部类Inner
public class Inner {
void getId() {
System.out.println(a);//内部类可以直接访问外部类的私有属性
}
void method(){
print();//也可以直接调用外部私有方法
}
}
}
package com.draft;
public class Application {
public static void main(String[] args) {
Outer obj1 =new Outer();//创建外部类对象obj1
Outer.Inner obj2 =obj1.new Inner();//通过外部对象obj1创建内部对象obj2
obj2.getId();
obj2.method();
}
}
运行结果:
异常机制(Exception)
什么是异常
Exception 意外
- 异常指程序运行中出现的意外状况, 如: 文件找不到\网络连接失败\非法参数…
- 异常发生在程序运行期间, 影响程序正常的执行流程
简单分类
- 检查性异常: 是程序员无法预见的, 最常见的是由用户错误引起, 如:打开一个不存在的(被删除)文件;编译时不能被简单忽略;
- 运行时异常: 可被程序员避免的, 可以在编译时被忽略;
- 错误ERROR: 不是异常, 是脱离程序员控制的问题, 在代码中通常被忽略;(如: 栈溢出, 错误就发生了, 编译时检查不到)
异常体系结构
- java 把异常当作对象处理, 并定义一个基类
Java.lang.Throwable
作为所有异常的超类 - 在java API已经定义了许多异常类, 分为 错误Error 和异常Exception 两大类
如何捕获异常?
Error
- Error类对象由Java虚拟机生成并抛出, 大多数错误与代码编写者所执行的操作无关;
- Java虚拟机运行错误(Virtual MachineError), 当JVM执行操作时内存不足(如栈溢出), 将出现
OutOfMemoryError
;出现后JVM一般会选择终止线程; - 还有Error发生在JVM试图执行应用时, 如类定义错误(
NoClassDefFoundError
)\链接错误(LinkageError
);这些错误不可查, 在应用程序的控制及处理能力之外, 而且绝大多数是程序运行时不允许出现的状况;
Exception
大体可分为运行时异常(RunTimeException
)和非运行时异常
RunTimeException
:
1. ``ArrayIndexOutOfBoundsException``:**数组下标越界**
2. ``NullPointerException``:**空指针异常**
3. ``ArithmeticException``:**算数异常**
4. ``MissingResourceException``:**丢失资源**
5. ``ClassNotFoundException``:**找不到类**
这些异常是不检查异常, 程序中可以选择捕获或是不处理
- 这些异常一般是由逻辑错误引起的, 程序应从逻辑角度避免此类问题;
- 与Error的区别:
- Error是致命的错误, 程序无法控制处理, 会导致JVM终止线程;
- Exception通常是可被程序处理的, 并且应在程序中尽可能去处理这些异常;
Java异常处理机制
抛出异常
捕获异常
异常关键字:
try catch finally throw throws
-
try catch finally
public static void main(String[] args) {
int a = 1;
int b = 0;
try{//监控区域
System.out.println(a/b);
}catch (ArithmeticException e){//catch捕获异常, 参数为异常对象名, 捕获到异常执行代码
System.out.println("ArithmeticException");
}catch (Exception e){
System.out.println("Exception");
} catch (Throwable e){//可以有多个catch, 范围依次从小到大
System.out.println("Throwable");
} finally{//处理善后, 有无异常均执行
System.out.println("done");
}
运行结果:
快捷键:ctrl + alt + T
打开环绕方式
throw throws
- throw: 方法中抛出异常
public class Draft {
public static void main(String[] args) {
new Draft().test(1,0);//匿名对象快速测试
}
public void test(int a,int b){
if(b==0){
throw new ArithmeticException();//异常情况时主动抛出异常,一般在方法中使用
}
// System.out.println(a/b); 注释,方法未执行(主动抛出)
}
}
- throws : 方法上抛出异常
public class Draft {
public static void main(String[] args) {
try {
new Draft().test(1, 0);//匿名对象快速测试
} catch (ArithmeticException e) {
e.printStackTrace();//打印异常信息
}
System.out.println("move on");//测试程序能否继续执行 结果:打印成功,程序继续执行
}
public void test(int a,int b) throws ArithmeticException{//throws用在方法上(在方法中无法处理时)
a=a/b;
System.out.println("move on too");//测试异常后方法是否结束 结果:方法结束
}
}
运行结果:
自定义异常
使用较少
如何自定义异常:
使用java内置的异常类可以描述在编程时出现的大部分异常情况, 自定义异常类只需要继承Exception类:
具体步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出的方法中处理异常,可以使用try\catch语句捕获并处理;
否则在方法声明处通过throws关键字指明抛出给方法调用者的异常, 继续进行下一步操作 - 在出现异常方法的调用者中捕获并处理异常
实际应用异常总结
- 处理运行异常时, 采用逻辑去合理规避的同时辅助以try-catch处理
- 在多重catch的最后, 可以加上catch(Exception)避免异常的遗漏
- try-catch也可以处理不确定代码的潜在异常
- 处理异常, 切忌简单调用
printStackTrace()
打印输出 - 具体如何处理异常, 由不同业务需求异常类型确定
- 尽量添加finally语句块去释放占用的资源