Java面向对象
面向对象
面向对象在开发中是一种运用对象、类、继承、封装、聚合、消息传递、多态等概念来构造系统的软件开发方法。
面向对象其实是相对于面向过程而言。
面向对象在开发中的优点:
减少软件的复杂性
- 可维护性
- 可扩展性
- 可重用性
表述非常自然
- 将数据和功能并在一起考虑
- 分析和实现的隔阂变得非常小
面向对象特征:
- 封装(encapsulation)
- 继承(inheritance)
- 多态(polymorphism)
对象
对象是系统中用来描述客观事物的一个实体,它是构成系统的基本单位。
对象的性质:
- 封装性
- 自治性
- 通信性
- 被动性
对象的特性:
- 对象的属性和方法称作对象的特性
- 属性值即对象的状态
- 方法即对象的行为
对象的标识:
缩写(OID)
是将一个对象和其它对象加以区分的标识符
一个对象标识和对象永久结合在一起,不管这个对象状态如何变化,一直到对象消亡为止。
类
类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分
- 对象按照不同的性质划分为不同的类
- 同类对象在数据和操作性质方面具有共性
- 把一组对象的共同特性加以抽象并存贮在一个类中
- 类是对象之上的抽象,有了类之后,对象则是类的具体化,是类的实例
- 类是静态概念,对象是动态概念
类的作用:
- 一是作为对象的描述机制,刻划一组对象的公共属性和行为
- 二是作为程序的基本单位,它是支持模块化设计的设施,并且类上的分类关系是模块划分的规范标准
类与对象:
- 属于某个类的对象称为该类的一个实例(instance)
- 类和对象间具有instance-of关系
- 一个实例是从一个类创建而来的对象
- 类描述了这个实例的行为(方法)及结构(属性)
类的定义格式:
class {
//属性...
//方法...
}
代码示例:
//定义Person类
class Person {
//定义属性name和age
String name;
int age;
//定义方法speak,可以定义具有返回值的方法
public void speak() {
System.out.println(("name=" + name + ",age=" + age));
}
}
方法
- 定义于某一特定类上的操作与规则
- 具有同类的对象才可为该类的方法所操作
- 这组方法表达了该类对象的动态性质,而对于其它类的对象可能无意义,乃至非法规则,说明了对象的其他特征之间是怎样联系的,或者对象在什么条件下是可行的
- 方法也称作行为(behavior)
方法格式:
权限修饰符 返回值类型 函数名(){
//业务逻辑
}
代码示例:
//定义返回值类型为String,权限为public的toStr方法
public String toStr() {
//返回值
return name + " : " + age;
}
类变量
类中局部变量和成员变量(类变量)
作用范围:
- 局部变量:这个变量所属的花括号
- 成员变量:这个变量随着对象的消失而消失,当java的垃圾回收机制把成员变量 所属的对象回收之后,这个变量就消失了
存储位置:
- 局部变量:栈内存中
- 成员变量:堆内存中
初始化值:
- 局部变量:在使用的时候必须有值
- 成员变量:不需要初始化值。因为它存在于堆内存中。默认会被初始化、。
- String null
- int 0
使用场景:
- 如果一个变量只需要在函数内部使用,那么就可以定义在函数内部。
- 如果后期还需要通过对象操作这个变量,那么只能定义在成员位置了。
static
静态使用场景:
- 针对所有对象的属性值都一样的时候,这个属性就可以使用static修饰,这个时候这个属性就存在于方法区中的静态区中。被所有对象共享。
- 针对每个对象特有的属性,这个就不能使用static修饰了。
针对静态修饰的变量有两种调用方式
- 1:使用对象进行调用
- 2:直接使用类名调用。
生命周期:
静态区中的变量(所属于类的) > 堆内存中对象里面的变量(所属于对象的) > 栈内存中变量(所属于当前的花括号)
注意:
在静态方法内部职能调用静态属性,如果想要调用非静态属性,必须先要获取到这个属性所属的对象。
静态使用场景注意事项:
成员变量
普通成员变量(实例变量)
- 所属于对象,必须先创建对象,才能使用这个变量,这个变量是存在于堆内存中,它的生命周期和对象一样,对象消失,实例变量才消失。
- 所属于类,不需要对象,可以直接通过类名来调用,这个变量存在于方法区的静态区中,它的生命周期和java虚拟机一样,虚拟机执行结束,类变量才消失。
成员函数
普通成员函数
- 可以调用普通成员变量和静态成员变量。
静态成员函数
- 只能调用静态成员变量,因为当静态成员函数被调用的时候,可能对象还不存在,所以不能调用对象中的特有属性。
- 在静态成员函数中不能使用this关键字。
main函数(主函数)
main函数是JVM运行java程序的入口。
格式:
public static void main(String[] args) {
//业务逻辑
}
每个结构表示的意义:
- public:是一个权限修饰符,最大权限,因为这个方法需要被虚拟机所调用,所以这个函数的权限应该设置为最大
- static:因为java虚拟机在调用这个方法的时候,还没有对象,所以需要静态
- void :因为虚拟机不需要返回值
- main:就是一个函数名,一个标识符,只不过比较特殊,可以被java虚拟机识别
- String[]:表示是一个字符串数组,
- args:参数名称//arguments
静态代码块
格式:
static{
//业务逻辑
}
静态代码块一般放在类中,和构造代码块平级
作用:
可以负责对类进行初始化。
静态代码块什么时候执行:
当类被加载进内存的时候,静态代码块执行。
Java面向对象三大特性
封装 继承 多态
封装
封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装是一种信息隐藏技术,在java中通过关键字private实现封装。什么是封装?封装把对象的所有组成部分组合在一起,封装定义程序如何引用对象的数据,封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。
- 对象的数据封装特性彻底消除了传统结构方法中数据与操作分离所带来的种种问题,提高了程序的可复用性和可维护性,降低了程序员保持数据与操作内容的负担。
- 对象的数据封装特性还可以把对象的私有数据和公共数据分离开,保护了私有数据,减少了可能的模块间干扰,达到降低程序复杂性、提高可控性的目的。
代码示例:
//定义Person类
class Person {
//封装的属性
String name;
int age;
//封装的方法
public void speak() {
System.out.println(("name=" + name + ",age=" + age));
}
}
继承
使用关键字extends实现。
格式:
class Zi extends Fu{}
继承的好处:
- 1:简化代码量,简化书写。代码看起来比较简洁
- 2:让类与类之间产生了关系,所以导致了面向对象的第三个特征,多态的出现。
继承的特点:
- java只支持单继承,不支持多继承。
- java里面支持多层继承。
到底什么时候使用继承:
- 1:可以分析两个类之间是否有包含关系,如果有,就可以继承。
- 2:先假设让某一个类继承另一个类,看一下继承过来的功能是不是自己应该具备的,如果是,可以继承,如果不是,则不能继承。
继承中的成员的特点
成员变量
- this关键字:代表当前对象的引用
- super关键字:代表父类的存储空间
this:是为了区分成员变化和局部变量重名的情况
super:是为了区分子父类中成员变量重名的情况
成员函数
当子父类中出现相同函数的时候,会出现函数的覆盖(重写,复写),也就是子类中的函数覆盖了父类中的同名函数
这个就是函数的重写(overwriter)
函数的重载(overload)
依据:只和参数列表有关
如果子父类中函数相同的时候,需要使用super调用父类中的同名函数
构造函数
默认情况下子类在创建对象的时候都会调用父类的无参的构造函数。需要使用super语句
注意,这个super语句默认是在子类的构造函数的第一行,没有显示,如果显式调用super语句的话,也必须写到子类构造函数的第一行。
this()和super()的用法类似
- this():表示调用当前类中的空参构造函数
- super():表示调用父类中的空参构造函数
那也就意味着,在子类的构造函数中不能同时存在this和super语句。
小结:
- 在子类的构造函数中,可以调用自己的构造函数,也可以调用父类的构造函数
- 如果需要在子类的构造函数中调用父类的有参构造函数,必须手工显式指定 super(name);
- 子类在创建对象的时候,一定会先调用父类的构造参数进行初始化。
代码示例:
//定义Person类
class Person {
//定义类的属性
String name;
int age;
//定义类的方法
public void speak() {
System.out.println(("name=" + name + ",age=" + age));
}
}
//继承Person类
class employee extends Person{
@Override
public void speak() {
System.out.println("this is employee");
}
}
多态
体现:
- 父类引用或者接口引用指向子类对象
Animal a = new Cat();//animal是cat的父类
Inter i = new InterImpl();//interimpl是inter接口的实现类
作用:多态的存在提高了程序的扩展性和后期可维护性
前提:类与类之间需要存在继承或者实现关系,要有覆盖操作
弊端:只能调用父类或者接口中已经定义的功能,在执行的时候会执行对应子类或者实现类中的功能。
类型强制转换动作和判断实例动作(instanceof)一般会在一块使用。
if(c instanceof Dog){
Dog d = (Dog)c;
d.lookDoor();
}
多态中成员的特点:
成员变量
编译的时候,看等号左边
执行的时候,看等号左边
非静态成员函数
编译的时候:看等号左边
执行的时候:看等号右边
静态函数
编译的时候,看等号左边
执行的时候,看等号左边
小结:
- 成员变量和静态函数:编译和执行都看等号左边
- 非静态成员函数编译看左边,执行看右边
final关键字
final关键字可以修饰不同东西
1:修饰类
这个类就是一个最终类,不能被继承。
2:修饰函数
这个函数就是一个最终函数,在子类中不能被覆盖。
3:修饰变量
这个变量的值就是一个最终值,不能被修改,只能被显式初始化一次。
final修饰的变量必须要被初始化。
一般使用final修改一个常量的时候会和static组合使用,注意:static和final的位置可以互换
如果在函数上定义static和final的时候,一般放在public后面 返回数据类型前面
小结:
- 1:当类中的所有函数不希望被重写的话,可以使用final修饰这个类,就变成了最终类,不能被继承。
- 2:当类中某一些方法不希望被重写的时候,需要在方法定义中加上final
- 3:如果变量的值是一个固定值,或者说是一个常量,这样可以使用final修改,一般还会加上static
抽象类
如果类中有抽象函数,那么类也必须是抽象的。
抽象类的特点:
- 1:抽象类不能被new,因为调用抽象函数没有意义。
- 2:如果父类中有多个抽象函数,子类要么全部实现,要么子类定义成抽象的。
- 3:抽象类和普通类的区别只是说抽象类中有抽象方法而已,那也就是说抽象类中是有构造函数的,因为子类继承这个抽象类的时候,也需要Udine抽象类中的属性进行初始化。
- 4:抽象类中可以没有抽象方法
- 5:继承中的覆盖注意事项(子类想要重写父类中的函数,子类必须要保证自己的函数的访问权限大于等于父类中需要覆盖的函数的访问权限)
接口
特点:
- 接口中的方法全部都是抽象方法,
- 相当于是抽象类的一个特殊体现形式
成员特点:
成员函数
接口中的所有函数默认情况下都是抽象的,并且权限修饰符默认都是public,所以在定义的函数的时候,public abstract都可以省略
成员变量
默认情况向接口中的所有变量都是被public static final修饰
特性:
类只能单继承
class A extends B//只支持这中格式
接口可以多实现
class A implements B,C,D//支持实现多个接口 BCD 都是接口
抽象类(普通类)里面一般定义的东西:
都是一些事物共性的属性或行为
接口里面一般定义的东西:
一些扩展的东西
class A extends AbstractDemo implements Inter,Inter2{
//业务逻辑
}
继承和实现的区别:
继承:extends
一个类继承一个抽象类,因为抽象类中可以存在非抽象方法,所以子类可以把父类中的非抽象方法直接继承过来使用。
实现:implements
因为接口中全部都是抽象方法,这些方法全部都需要进行实现,所以说一个类需要实现一个接口。
多继承的特性(了解)
interface A{}
interface B{}
interface C extends A,B{}//正确的 接口之间可以多继承
class A1{}
class B1{}
class C1 extends A1,B1{}//错误的,java中的类不支持多继承
object类中的方法
boolean equals(Object obj)
:比较两个对象是否相等,默认是比较内存地址值,这个意义不大,所以在工作中如果需要对对象进行比较的话,一般会重写此方法,实现自己的比较逻辑。
String toString()
:默认会打印对象的名称和内存地址值,但是打印这个意义不是很大,所以工作中一般会进行覆盖,在这个方法中把对象的基本属性信息给打印出去
Class getClass()
:获取当前对象的字节码文件对象,然后根据这个对象就可以获取字节码中保存的类的名称等信息getName和getSimpleName
int hashCode()
:返回的是对象在内存中的地址值
内部类
格式:
class Outer//外部
{
private int num;
class Inter//内部类,可以在成员位置
{
show(){
}
}
public void method(){
Inter inter = new Inter();
inter.show();
}
}
特点:
- 1:内部类可以使用外部类中的所有属性,包括私有的
- 2:如果外部类向访问内部类中的成员的话,默认情况下是需要创建内部类对象的
什么时候需要定义内部类呢?
假设A类可以随意访问B类中的属性,但是B类在访问A类中的属性的时候,需要创建A类的对象,才能调用
class B{
class A{
}
}
class Body{//身体
priavte class Heart{//心脏
}
public Heart method(){
if(是医生){
return new Heart();
}
return null;//Heart h = null;
}
}
如何在main函数中获取内部类对象(前提条件:内部类没有私有化)
//外部类名.内部类名 变量名 = 外部类对象.内部类对象
//内部类和函数都是普通的
//Outer.Inter io = new Outer().new Inter();
//io.show();
//如果内部类是静态的时候
//Outer.Inter io1 = new Outer.Inter();
//io1.show();
//如果内部类和调用的函数都是静态
Outer.Inter.show();
注意:
- 如果内部类中有静态成员,那么这个内部类也需要静态。
- 如果内部类中的函数是静态的,那么在这个函数内部就只能调用外部类的静态成员了。
内部类也可以放在函数内部
在这个位置的时候,需要注意:如果这个局部内部类访问到了函数的局部变量的时候,这个局部变量必须是final类型的。
内部类的命名:
Outer$1Inter.class
Outer$Inter2.class
Outer$1Inter.class
Outer$1Inter3.class
异常
Throwable
- Error
- Exception
处理方法有两种:
1:try-catch代码块
try{
//待检测的代码,也就是在运行时可能出现问题的代码
}catch(Exception e){//Exception e = new ArithmeticException();
//对异常情况进行处理
}
2:throws Exception
工作中针对异常到底使用哪种处理方案呢?
如果这个异常我能处理,就处理,不能处理的就抛出去。
jvm默认对异常的处理方式是这样的
- 1:调用异常对象的printStackTrace(),打印异常出现的位置,具体的错误信息等内容
- 2:退出程序
Exception包含两种异常
编译时异常:在编译的时候,如果没有处理,程序就会报错
如果调用的函数抛出了编译时异常,那么在使用这个函数的时候,必须要对这个异常进行处理。
包含Exception和(非runtimeException及其子类)
非编译时异常(运行时异常):编译时不会报错,运行的时候才会报错
这种异常在调用的时候不需要进行处理。因为一般抛出这种异常的时候是想希望让程序停掉。
runtimeException 以及子类
运行时异常,一般不建议使用try-catch进行捕获处理,如果真想处理,也可以使用try-catch
什么时候抛出runtimeException:
下面例子中在调用div之后的代码的运算逻辑需要使用x(除法运算的结果)的值,也就是说,
如果除法运算出错了,那么后面的代码再执行就没有意义了。
那么这个时候,就可以在div方法上抛出一个运行时异常,main方法再调用的时候就不需要进行处理了,
如果除法运算在执行的时候真是出现了问题,那么就希望把程序停掉即可。
class Demo11{
public static void main(String[] args) {
Test t = new Test();
int x = t.div(10,0);
System.out.println(x);
System.out.println("hehe"+x);
}
}
class Test{
//除法运算
public int div(int a,int b) throws RuntimeException {//这也是一种对异常的处理方法
return a/b;
}
}
try-catch-finally
组合可以有下面这几种
- try-catch
- try-finally:这样对代码处理之后,如果是编译时异常,还是需要进程throws的,catch里面才是真正处理的代码。
- try-catch-finally
- try-catch-catch-catch.......-finally:针对函数抛出多个异常类,这样在处理的时候,可以分别进程catch,也可以catch一个顶层父类(Exception)
如果函数抛出的多个异常有父子关系,那么在写多个catch语句的时候,子类的异常需要先捕获,父类异常写在最下面的一个catch中。
父子类中进行函数覆盖的时候,如果父类的函数中抛出了异常,那么子类只能抛出父类异常的子类或者子集,子类中不能抛出未知的异常
如果子类中确实有新的异常产生,那么只能try-catch处理掉。
throws和throw
- throws:用在函数上,用于向外声明异常,让上一级处理。后面跟的是异常类,可以跟多个
- throw:用在函数内部,用户抛出异常对象。让函数处理这个异常。
设计模式
表示是解决某一个类问题最行之有效的方法。
Java有23种设计模式
比如单例设计模式
:这个设计模式可以保证在内存中一个对象只存在一份。
例子:现在有A程序和B程序,
使用场景:
- 1:保证其他地方不能创建这个config的对象,
- 2:我们自己创建一个对象,把这个对象对外开放出去
- 3:对外提供公共的访问方式
代码实现过程:
- 1:私有化构造函数
- 2:在类中创建一个私有对象
- 3:对外提供一份public的静态方法,把刚才创建的对象返回出去