Java面向对象
Java面向对象(Object-Oriented)
- 本质:以类的方式组织代码,以对象的方式封装数据。
面向过程:步骤清晰简单,适合处理一些较为简单的问题
面向对象:物以类聚,分类的思维模式,思考问题首先解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类下的细节进行面向过程的思考。
面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
- 对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是,具体到微观操作,仍然需要面向过程的思路去处理。
三大特性
- 封装
- 继承
- 多态
从认识论的角度考虑是先有对象后有类。对象是具体的事物,类是抽象的,是对对象的抽象。
从代码运行的角度考虑是先有类后有对象。类是对象的模板。
方法(加深)
方法的定义
- 修饰符
- 返回类型
- break:跳出switch,结束循环和return
- 方法名:规范
- 参数列表:参数类型和参数名
- 异常抛出
方法的调用
-
静态方法
-
非静态方法
static是和类一起加载的。非静态方法需要实例化后才能存在。
-
形参和实参
-
值传递和引用传递
-
this关键字
对象
对象类型 对象名 = 对象值
Student student = new Student();
类和对象
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。
对象是抽象概念的具体实例
张三是人的一个具体实例
能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造方法
- 必须和类的名字相同
- 必须没有返回值,也不能写void
构造器是重点
构造器
快捷键alt+insert
-
和类名相同,没有返回值
-
一个类即使什么都不写,它也会存在一个方法,无参构造
-
如果定义了有参构造,要显式的定义无参构造
-
使用new关键字,本质上就是在调用构造器。初始化对象的值
对象内存分析
普通对象实例化
- 在堆中的方法区加在类Person的一些数据。
- 在栈中开辟空间,申请一个地址存放p对象,这个p对象指向堆中的实例对象的地址,但是现在还没有。
- 在堆中开辟空间,分配地址BE2500,对对象的属性进行默认初始化,然后进行程序员的显示初始化。
- 构造函数进栈
- 初始化完毕,将堆内存的地址赋给栈内的引用变量p。构造函数出栈。
子类对象实例化
后续看JVM可回顾这里
封装
该露的露,该藏的藏
设计程序要追求“高内聚,低耦合”
高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅暴露少量的方法给外部使用。
封装(数据的隐藏):禁止直接访问一个对象中数据的实际表示,而应通过操作借口来访问,这称为信息隐藏。
属性私有,get/set
-
提高安全性,保护数据
-
隐藏代码的实现细节
-
统一接口
-
提高系统维护性
继承
属性:不可被重写,只可被隐藏
方法:会被重写,不会隐藏
Java只有单继承,没有多继承
继承是类和类之间的关系。除此之外还有依赖、组合、聚合等
子类默认调用了父类的无参构造
super是调用了父类的构造方法
super必须只能出现在子类的方法或者构造方法中
super和this不能同时调用构造方法
vs this
代表对象
this代表调用者本身这个对象
super代表父类对象的应用
前提
this没有继承也可以使用
super只有在继承条件下才可以使用
构造方法
this()本类的构造
super()父类的构造
多态
前提
- 存在继承或者实现关系
- 重写
- 父类引用指向子类对象
成员方法
- 编译时:查看引用变量所属的类中是否有所调用的方法
- 运行时:调用实际对象所属的类中的重写方法
成员变量
- 不具备多态性,只看引用变量所属的类
所以多态跟new有关。
重载:本类中的同名方法,相同的名称实现不同的逻辑。
对象的多态---直接应用在抽象类和接口上
编译时类型和运行时类型
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋值给该变量的对象决定
如果编译时类型和运行时类型不一致,就出现多态
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向多种不同类型的对象
Person p = new Person();
Person e = new Student();//Person类型的变量e,指向Student类型的对象
向上转型:子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象
类变量类型
- 一个引用类型变量如果声明为父类的类型,但实际引用的是子类的对象,那么该变量就不能再访问子类中添加的属性和方法
多态意思就是,以子类的模板生成了一个父类的实例。同名方法是子类的方法。同名属性是看左边,类型是谁就是谁的。子类独有的方法和属性不能访问(因为没有重写条件,无法触发多态)。
方法类型:虚拟方法调用
正常的方法调用。编译时和运行时一致
Person p = new Person();
p.getInfo();
Student s = new Student();
s.getInfo();
虚拟方法调用(多态)
Person e = new Student();
e.getInfo();//调用的是Student类的getInfo()方法
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的方法。-----动态绑定
方法的重写和重载
重写:子类对父类方法的覆盖,属性是无法覆盖的,属性是隐藏。
重写都是方法的重写,和属性无关
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小 public>protected>default>private
- 抛出的异常:范围可以被缩小,但不能扩大ClassNotFoundException<Exception
重写:子类的方法和父类一致,但方法体不同。
父类的功能,子类一不定需要,或者不一定满足。
//Static方法的调用只和左边定义的数据类型有关
A a = new A();
a.text();//A
//父类的引用指向了子类
B b = new A();
b.text();//B
静态方法是类的方法,非静态方法是对象的方法。
Person p = new Student
//Person类p = new Student对象
//编译时 运行时
instanceof
A instanceof B
实例对象是否属于类B
对于正常继承关系
子类instance父类为true
对于多态
看右边实例对象
Object object = new Student;
//object实际上是Student的实例。
object instanceof Student;//true
//实际为Student instanceof Student
object instanceof Person;//true
//实际为Student instanceof Person
object instanceod Object;//true
//实际为Student instanceof Object
抽象类
用abstract修饰
- 可以没有抽象方法,用abstract修饰就是抽象类了
- 有抽象方法的一定就是抽象类
- 抽象类无法被实例化
- 抽象类的子类必须实现它的抽象方法
不用写方法的实现
主要用来捕捉子类的通用特性的,作为子类创建的模板
如果有一个接口,五个实现类,现在的需求可能要往接口加一个方法,这样要改变五个类,但是需求只需要改动其中两个实现类,可以再定义一个抽象类去实现这个接口,在抽象类中新增这个方法,然后其他两个实现类实现这个抽象就好了
==和equals方法
equals本质上等同于==
但是在引用比较上,equals重写了部分的方法,使之变为了值比较。
总结:==是用来引用比较的,equals是用来值比较的。目前有String、File、Date及包装类(WrapperClass)重写了equals的值比较,如果想将某类的引用比较改成值比较,需要重写该类的equals
String对象有两种创建方法
字面量创建String对象和new创建String对象
包装类和toString
包装类:针对八种基本数据类型定义相应的引用类型
可以有类的特点,调用类的方法。
主要用来转换基本数据类型和字符串
基本数据类型对应的包装类
- boolean ---> Boolean
- byte ---> Byte
- short ---> Short
- int ---> Integer
- long ---> Long
- char ---> Character
- float ---> Float
- double ---> Double
装箱和拆箱
-
装箱
通过参数构造包装类对象
Integer i = new Integer(500); Float f = new Float("4.56"); Long l = new Long("200"); //简便方法 Integer i0 = 112;//自动装箱 Boolean b0 = false;
-
拆箱
调用包装类的.xxxxValue()方法
int i0 = i.intValue(); boolean b0 = b.booleanValue(); //简便方法 int i0 = i; boolean b0 = b;
字符串和基本数据类型转换
-
通过包装类的构造器实现
int i = new Integer("12");//本质上还是调用了parseXxxx静态方法。因为Integer的String有参构造使用的就是parseXxxx方法。
-
通过包装类的parseXxxx(String s)静态方法
int i = Integer.parseInt("123");
基本数据类型转换成字符串
-
调用字符串重载的valueOf()方法
String str = String.valueOf(2.34f);
-
更直接的方式
String str = 5+"";
设计模式的单例设计模式
- 单例类只有一个实例
- 单例类必须自己创建自己的唯一实例
- 但李磊必须给其他所有对象提供这一实例
意图:保证一个类仅有一个实例,并且提供一个访问它的全局访问点
主要解决:一个全局使用的类频繁的被创建和销毁
何时使用:当你想控制实例数目,节省系统资源的时候
如何解决:判断系统是否已经有这个单例,有则返回,无则创建
关键代码:构造函数是私有的
实现:创建一个单例类,有它的私有构造函数和本身的一个静态实例,(因为静态只会创建一次)。提供静态方法,供外界获取它的静态实例。
饿汉式的单例模式
先静态创建一个实例,等别人通过方法调用。
懒汉式的单例模式
最开始不创建实例,等到第一个人调用时,创建对象,之后所有调用都返回这个对象
代码块
在new Person的时候
- 先加载类的默认初始化和显示初始化。即成员变量
- 执行非静态代码块的代码
- 执行构造函数
静态代码块:static修饰的代码块
- 可以有输出语句
- 可以对类的属性声明进行初始化操作
- 不可以对非静态的属性初始化,不可以调用非静态的属性和方法
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行
- 静态代码块的执行要先于非静态代码块
- 静态代码块只执行一次
非静态代码块
- 可以有输出语句
- 可以对类的属性声明进行初始化操作
- 可以调用静态和非静态的变量或方法
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行
- 每次创建对象的时候,都会执行一次,且先于构造器执行
final关键字
final修饰的类不能被继承。final是一个最终的类。
final修饰的方法不能被重写
final修饰的变量为常量,常量必须显式赋值
模板方法设计模式
当功能内部一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,让子类去实现。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给子类实现,就是一种模板模式。
步骤
- 创建一个抽象类,它的模板方法被设置被final,里面会有一些未被实现的抽象方法。
- 创建子类继承抽象类,实现上述的抽象方法
接口implements
为了实现多继承
接口是抽象方法和常量值的定义的集合
接口是一种特殊的抽象类,这种抽象类只包含常量和方法定义,而没有变量和方法的实现
一个类可以实现多个接口,接口也可以继承其它接口
实现接口的类必须提供接口中所有方法的具体实现内容,才能被实例化,否则,仍为抽象类
先继承后实现
接口可以被接口继承,不能被实体类继承
接口的意义
当一个子类实例继承一个抽象父类时。
如果想要给父类增加抽象方法,则子类必须要实现它。
现在定义一个接口写抽象方法
让父类去实现抽象方法
让子类可选择实现或者不实现该接口
工厂模式
- 有一个抽象父类
- 子类继承父类
- 工厂类(给工厂提供子类属性和共有属性,产生子类实例。
内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
可以解决Java不能多继承的问题。
比如classA,想要继承classB和classC,直接是不可以的
但是我们可以在classA里面,写两个内部类,分别继承classB和classC,然后重写方法。内部类作为classA的成员,可以被调用。这样可以通过a.innerb.方法来实现原本的a.方法
调用外部属性
特性:
- 内部类作为成员
可以声明为final
可以声明为private或protected
可以声明为static,但此时不能使用外层类的非static的成员变量
- 内部类作为类
可以声明为abstract类,可以被其他内部类继承
注:非static的内部类的成员不能被声明为static,只有在外部类或者static的内部类中才可声明static成员