面向对象
类
类和对象的概念
- 类在现实中是不存在的,是一个模板 概念
- 类代表了一类事物
- A和B有共同的特征,抽象总结出一个类
- 对象是实际存在的个体
- 类——> 对象 (实例化)
- 对象——> 类 (抽象化)
类描述的是对象的共同特征
状态信息 ——> 属性
动作信息 ——> 方法类的定义
修饰符列表 class 类名{ //属性 //方法 }
java中所有的class类都是引用数据类型
属性通常是以一个变量的形式来完成定义的
对象的创建与使用
对象 == 实例,实例变量 == 对象变量
通过一个类可以实例化多个对象
语法:
new 类名(); // new 创建对象
例如 Student s = new Student();
对象:在堆内存开辟的内存空间为对象
引用:引用是一个变量,在这个引用里保存了java对象地址
访问实例变量的语法格式
- 面向对象的分析 OOA
- 面向对象的设计 OOD
- 面向对象的编程 OOP
构造方法
介绍
- 构造方法又被称为构造器/构造函数/Construstor
- 语法:
【修饰符列表】 构造方法名(形参){构造方法体}
构造方法名必须和类名一致
- 每一个类都有一个默认的构造器(缺省构造器/无参数构造方法)
- 当用户手动提供了构造器,那么系统将不再提供无参构造器
- 所以手动先写上无参构造
- 构造方法支持重载
面向对象
三大特征:封装、继承、多态
封装
什么是封装?
- 对外提供简单的操作入口(复杂性封装)例如:照相机
- 以后的程序可以重复的使用
- 封装后,对事物本省提高了安全性
- 封装后在会形成独立体
封装步骤
- 私有化
- 使用private来进行修饰
- private 表示私有的,所有的数据只能在本类中访问。
- 需对外提供入口
- 提供入口
- set(修改)
- get(获取)
get/set命名规范
set
public void set+属性名首字母大写(形参(只有一个)){ } 例如: public void setAge(int a){ age = a; }
get
public 返回值类型 get+属性名首字母大写() { } 例如: public int getAge(){ return age;}
setter 和 getter中没有static关键字;
继承基础 extends
作用
- B类继承A类
- 则A类为父类/超类/基类(superclass)
- B类称为子类/派生类/拓展类(subclass)
- java中只支持单继承,不支持多继承也就是说不能这样写 class B extends A , C
- 虽然不支持多继承,但有时候会产生间接继承的效果
- java中规定,子类继承父类,除了构造方法不能被继承外,其余的都会继承但是父类私有的属性无法再子类中直接访问(需要用get,set方法)
- Java中没有继承任何东西的类,默认继承Object类(老祖宗类)
- 缺点: 耦合度高,父类修改,子类受牵连
class B extends A{ // java语句 }
什么时候使用继承?
§ 当子类继承父类之后,继承过来的方法无法满足子类的需求时子类有权利对父类继承过来的方法进行重写
§ 例如:父类中的方法是 动物在移动,而子类的业务需求是 鸟儿在飞翔 ,
- 含义
- 方法覆盖就是将子类继承父类的方法进行重写/覆盖掉
- 方法覆盖后调用子类的时候一定执行覆盖之后的方法
最好将要重写的方法复制过来; idea 可以alt+ins 生成
什么时候会构成方法覆盖
- 两个类必须要有继承关系
- 重写之后的方法和之前的方法对比
§ 方法名相同
§ 返回值类型相同
§ 形参相同
- 权限不能低,可以高
- 重写以后的方法不能比之前的方法抛出更过的异常,可以更少
注意
- 方法覆盖只针对方法,和属性无关
- 私有方法无法覆盖
- 构造方法不能被继承,所以不能被覆盖
- 只针对实例方法。覆盖静态方法毫无意义。
多态
多态基础一
两个概念
- 向上转型 子—>父
- 向下转型 父—>子
什么时候使用向下转型,如果想访问的方法是子类中特有的方法就必须 向下转型
向下转型语法
Animal a = new Cat(); if( a instanceof Cat){ Cat b = (Cat)a; b.doSome(); }
类型转换异常 : java.lang ClassCastException
注意:无论是向上转型还是向下转型都需要有继承关系
多态概念
多种形态
- 父类型引用指向子类型对象
- 编译阶段:绑定父类的方法(静态绑定)
- 运行阶段:(动态绑定)子类型对象的方法
多态2
作用:
降低程序的耦合度,提高拓展力
如何避免类型转换异常的发生
instanceof 关键字
instanceof 可以在运行阶段动态判断引用指向的对象的类型
语法 (引用instanceof 类型)
运算结果只能是 true/false
只要是向下转型必须都要用instanceof 判断
空指针异常
例如: Student s = new Student();
s = null;
System.out.println(s.name);
如果引用为null , 那么访问这个引用里面的实例变量时会空指针异常
- 但是静态变量可以访问
- 空引用访问实例相关的数据一定会空指针异常
- 实例方法
- 实例变量
空指针异常:java.lang NullPointerException
Object源码
- 源码中一个方法以;结尾,并且修饰符列表中有native关键字
- 表示这个方法底层调用c++ 写的dll程序(动态连接库文件)
对比方法重载和方法重写
重载【override】
方法重载需要满足的条件
- 方法名相同
- 在同一类中
- 形参不同
- 形参不同
- 顺序不同
- 数据类型不同
重写【overwrite】
方法重写需要满足的条件
- 两个类必须要有继承关系
- 重写之后的方法和之前的方法对比
- 方法名相同
- 返回值类型相同
- 形参相同
- 权限不能低,可以高
- 重写以后的方法不能比之前的方法抛出更过的异常,可以更少
static
介绍
- 所有static关键字修饰的都是类相关的,类级别的
- 所有被static修饰的,都是用类名.的方式去访问
修饰
- 成员变量
- 实例变量 int i ;
- 静态变量 static int a;
§ 静态变量在类加载的时候初始化完成,不需要new对象,空间就已经开出来了
§ 静态变量存储在方法区内存
§ 一般的静态变量都需要赋值
§ 同时在构造方法上也不需要写上静态变量的实参
- 方法
- 实例方法 public void doOther(){}
- 静态方法 public static void doSome(){}
什么时候声明静态变量 什么时候声明实例变量呢
- 当一个类中,都有统一的一个变量(属性)
- 例如:中国人 类中,国籍都是中国,国籍可以声明为静态
- 此做法可以节省内存空间 static String country = “中国”;
- 一个对象一份实例变量
- 只能使用 引用.的方式去访问
- 所有对象一份是静态变量
- 可以使用引用. 但是不建议。最好类名.
什么时候声明静态方法,什么时候声明实例方法呢?
- 当这个方法中访问了实例变量,那么这方法一定是实例方法
- 在以后的开发中,工具类的一般都用带static的
优点 方便,不用new对象
结论
- 空引用并不会对静态变量和静态方法造成影响
- 静态方法/变量可以用引用.的方式进行访问,但是不建议
- 实例方法要想访问,必须要先new对象,然后引用. 去访问
- 空引用会对实例造成影响
static静态代码块
执行时间
- 在类加载的时候执行,且只执行一次
-
作用
在类加载前做一些准备工作 (特殊的类加载时机)
一个类中可以有多一个静态代码块 静态代码块可以访问静态变量,因为他们都是在类加载时执行
- 静态代码块不可以访问实例变量,因为实例变量在new对象之后才能访问
- 如果静态变量在静态代码块的下面,则无法访问 遵循自上而下的顺序
实例语句块
{ // 实例代码块 }
实例语句块在构造方法执行之前执行(自动执行)
每new一次对象执行一次
作用
- 对象构造时机
- 当几个构造器里有相同的语句时,可以使用实例语句块
this
介绍
- 一个对象一个this
- this 是一个变量,是一个引用 this 保存了当前对象的内存地址,
- 严格上说this就是当前对象
- this 存储在堆内存当中,对象的内部
- this只能使用在没有static的方法中,(谁调用该方法,this就是谁)
- this. 可省略 (默认访问当前对象)
- this 不能出现在静态方法中(this表示当前对象,而静态方法不存在对象)
this大部分可以省略,什么时侯不能省略呢
例如:
public void setAge (int age ){ this.age = age; }
为了区分局部变量和实例变量,这种情况必须加thisthis新语法 调用本类中的构造方法
语法:this(实参);
通过当前的构造方法去调用另一个本类中的构造方法
public Date (){ this(1970,1,1); } public Date(int year,int month,int day){ this.year = year; this.month = month; this.day = day; }
注意事项:
- this(实参) 调用的必须是本类中的构造方法
- this() 只能出现在构造方法的第一行,且只能出现一次
super
语法
- super(实参); 调用父类的构造方法
- super.方法名(实参) 调用父类的方法
-
super与this对比学习
super
super 能出现在实例方法和构造方法中
- super的语法是 super. super() super.方法名()
- super不能用在静态方法中
- super大部分可以省略
- super()只能出现在构造方法第一行,
- 通过当前类调用父类的构造方法(代码复用)
this
- this 能出现在实例方法中和构造方法中
- this的语法是 this. this();
- this不能出现在静态方法中
- this. 可省略 凡是为了区别变量时不可省略
- this()只能出现在第一行,通过当前的构造方法调用其他的构造方法
super
- 当一个构造方法第一行没有this() 没有super()时默认有一个super()
- 表示通过当前子类的构造方法调用父类的无参构造
- 所以必须保证父类中的无参构造必须有
- this() 和 super()不能共存
- Object类的构造方法一定会执行 100%
-
super() 的使用
作用:初始化父类型的特征
- 当在子类中想给父类的变量赋值的时候(封装后),
- 可用到子类的构造方法调用父类的构造方法
public B(String name,int age){ super(name,age) } // 调用父类的有参构造
super.
- super.name 访问的是父类的变量name
- this.name 访问的是当前对象的name
- super.什么时候不能省略
- 父类中有name
-
super 方法名();
调用父类的方法
- super.sport(实参) 调用的是父类中的sport方法
final
final可修饰方法、变量、类等
类:不可以被继承
方法:
不能被覆盖(重写)
被final修饰的变量只能赋一次值
引用:
- 该引用只能指向一个对象,并且它永远指向该对象
- 被final修饰的引用,该引用里面的数据是可以被修改的
实例变量 (少见):
- 不会赋默认值 必须手动赋值不然编错
- 这个手动赋值,在变量后面也可以,在构造方法里赋值也可以
常量
- final修饰的实例变量一般都是加static的(节省内存)
- static final 修饰变量,称为常量(全部大写 _ 连接)
例如
public static final String COUNTRY = “中国”;
存储在方法区
常量一般都是公开的
总结
类体中有
- 实例变量、实例方法
- 静态变量(static)静态方法(static)
- 构造方法(有参无参)
- 静态代码块
- 实例语句块
- 方法
- 所有实例相关的都是先创建对象 引用. 访问
- 所有静态相关的都是 类名. 访问
- 静态方法中要想访问实例变量先new对象
- 只要调用的方法a 和方法b 在同一类中
- this. 可省略(实例方法) 类名. 可省略(静态方法)
抽象类
介绍
- 抽象类无法实例化对象(不能new)
- 抽象类是类与类之间存在共同的特征,将这些特征抽象出来,来形成一个类
- 抽象类属于引用数据类型
语法:
【修饰符列表】abstract class 类名{ //类体 }
- 抽象类是用来被子类继承的
- 子类可以是抽象类
- 因为抽象类是要被继承的所以abstract和final不用共存(对立)
- 抽象类有构造方法,供子类创建对象使用
抽象方法
对抽象方法的理解
没有实现的方法,没有方法体的方法
特点
- 没有方法体,以分号结尾
- 修饰符中有 abstract 关键字
- 例:public abstract void doSome();
- 抽象方法只需声明,而不需实现某些功能
抽象类中不一定有抽象方法,有抽象方法的必须是抽象类(除接口)
抽象方法和非抽象方法可以同时存在
非抽象类继承抽象类,必须将抽象类里的方法全部实现(重写)
用abstract来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法。
接口
基础
【修饰符列表】 interface 接口名{ // 常量 抽象方法 } 接口中只能有 常量 和抽象方法
- 接口属于”引用数据类型”
- 接口是完全抽象的
- 抽象类是半抽象的
- 接口编译后也是class 文件
- 接口与接口之间支持多继承
interface A extends B,C{ }
- 接口中 public abstract 可以省略
public interface A{ double PI = 3.14; // 常量 int sum(int i ,int j); // 抽象方法 } 在接口中 :public static final double PI = 3.14 等同于 double PI = 3.14;
- 接口中抽象方法的public abstract可以省略。
- 接口中常量的public static final可以省略。
- 接口中方法不能有方法体
接口二
- 接口中所有的都是public 修饰的(公开的)
- 类与接口直接叫做实现,用implements 实现
例如 class A implements A ,B{
}
- 一个类支持实现多个接口
- 当一个非抽象类实现一个接口,必须将接口中所有的方法实现
- 继承和实现都存在时,代码写法
例如: class A extends B implements C{}
//A类继承B类 实现C接口
接口在实际开发中的作用
- 面向抽象 = 面向接口
- 有了接口就有了可插拔 ,拓展力变强
- 凡是能使用 has a 来描述的,同一以属性的形式存在 (实例变量)
- 接口可以解耦合
抽象类和接口的区别
访问权限/package
访问权限
- private :只能在本类中访问
- public : 在哪里都能访问
- protected :表示只能在本类,同包名子类中访问
-
修饰符【访问控制】可修饰
属性 (4个都可以)
- 方法 (4 个都可以)
- 类 ( public 默认 )
- 接口 (public 默认)
修饰符 | 本类 | 同包 | 子类 | 任意位置 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ |
× |
默认 | √ | √ | × | × |
private | √ | × | × | × |