JavaSE–面向对象整合
面向对象的三条主线
- 类和类员:属性,方法,构造器,代码块等
- 面向对象的三大特征:封装,继承,多态
- 其他关键字:this,super,abstract,interface,static, final,package,import
封装
封装性的体现:
- 我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getxxx)和设置(setxxx)此属性的值
- 不对外暴露的私有方法
- 单例模式
权限修饰符:(权限从小到大)
private , 缺省(不写) , protected , public
继承
一旦子类A继承了父类B,则子类A就获得了父类B中声明的结构(所有属性、方法等,包括封装性)
Java继承的规定
-
单继承性:一个类只能有一个父类
-
子父类是相对的概念
-
子类直接继承的父类,称为:直接父类。间接继承的父类称为间接父类(java中一个子类不存在多个父类,只有间接继承,即一个子类的父类是另一个类的子类)
多态
可以理解为一个事物的多种形态
也称为对象的多态性:父类的引用指向子类的对象。 eg:
class Person{
}
class Man extends Person{
}
class Women extends Person{
}
//对象的多态性,父类的引用指向子类的对象
Person p1 = new Man();
Person p2 = new Women();
多态性的使用:虚拟方法调用
有了多态性以后,在编译的时候,只能调用父类的声明的方法,而运行时,实际执行的是子类重写父类的方法
即:编译看左边,运行看右边
多态性的使用前提:①有类的继承;②有方法的重写
多态性属于运行时行为
多态小结 多态作用: 提高了代码的通用性,常称作接口重用 前提:需要存在继承或者实现关系, 有方法的重写 成员方法:
-
编译时:要查看引用变量所声明的类中是否有所调用的方法。
-
运行时:调用实际new的对象所属的类中的重写方法。
成员变量:不具备多态性,只看引用变量所声明的类。
-
如果没有多态性,则需要写很多重载的方法
-
多态性只使用于方法,不适用于属性
构造器
- 每一个类都有构造器
- 构造器的作用:①创建对象;②给对象初始化(属性)
说明:
1,如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2,定义构造器的格式:权限修饰符 类名(形参列表){}
public Person(){
}
3,一个类中可以有多个构造器,彼此构成重载
4,没有显式定义的才会有默认构造器,一旦显式的定义了构造器,系统则 不再提供空参构造器
5,一个类至少有一个构造器
6,默认构造器的权限和所在类的权限相同
属性赋值顺序:
①默认初始化
②显式初始化
③构造器中赋值
④通过“对象 . 属性”或“对象 . 方法”赋值
this关键字
-
this相当于:当前对象或当前正在创建的对象
-
用来区分重名的属性和形参
-
可以用来修饰:属性,方法,构造器
this 调用构造器:(根据参数列表调用)
this(); //调用空参的构造器
this(形参列表);
规定:调用构造器时,“this(形参列表)”必须声明在当前构造器的首行
构造器中不能用this调用自己,只能调其他的构造器
如果一个类中有n个构造器,则最多只能有n-1个this调用构造器,不能往回调
super关键字
-
super理解为:父类的(类似this用法)
-
super可以调用 属性、方法、构造器
-
可以在子类的方法或构造器中,通过 super . 属性 或 super . 方法的方式,显式的调用父类中声明的属性或方法。通常情况下,习惯省略super
-
特殊的,子类和父类定义了同名的属性时,此时若要调用父类的属性,必须使用super调用父类的
-
父类方法被重写后,需要使用super调用父类中的被重写的方法
super调用父类构造器
super(形参列表);
- super(形参列表)必须声明在构造器的首行
- this(形参列表)和super(形参列表)不能同时出现
- 默认调用super(空)
- 在类的多个构造器中,至少有一个类的构造器是用来super(形参列表)
方法详解
- 方法的重载
- 可变形参的方法
- 方法参数的值传递机制
- 递归方法
方法重载:
-
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
-
与返回值无关,只看参数列表且参数列表必须不同(参数个数或参数类型)
调用时,根据方法参数列表的不同来区别
以下方法都是重载:
public void getsum(int i , int j){
System.out.println(i + j);
}
public void getsum(double i ;double j){
System.out.println(i + j);
}
public void getsum(String i ,int i){
System.out.println(i + j);
}
public void getsum(int i ,String j){
System.out.println(i + j);
}
-
即使是参数列表顺序不同,也构成重载,根据传递参数顺序调用具体的函数
-
跟方法的权限修饰符,返回值类型,形参变量名,方法体都无关
-
通过对象调用方法时,如何确定某一指定方法:
先看方法名,如果有重载,再根据参数列表判断(方法名——>参数列表)
可变个数的形参:
1.JDK5.0新增内容,调用可变个数形参时,可以传入多个参数
2.具体使用:
格式:变量类型…形参变量名,eg:
public void show(String ... str){
return 0;
}
//调用时可以同时传递多个参数
test.show("hello","world","he");
- JDK5.0以前需要传递多个参数时,使用的是数组,因此可变形参新特性不能续数组共存(会构成重载)
//以下方法构成重载(表示的是同一个东西)
public void (String[] arr){
}
public void (String ... arr){
}
方法的形参传递机制:值传递
- 机制:如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址
swap(a,b);
只有参数类类型为引用数据类型(类的对象)时才能交换,eg:
class Data{
int m;
int n;
}
punlic static void main(String[] args){
Data data = new Data();
data.m = 10;
data.n = 20;
v.swap(data);
System.out.println(data m , data n);
}
swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
-
关于变量赋值:
如果变量是基本数据类型,此时传递的是保存数据的值
如果变量是引用数据类型,此时传递的是保存数据的地址
递归方法
方法内调用方法本身,例如,斐波那契数列,汉诺塔问题
方法的重写
override / overwrite
-
重写:子类继承父类以后,可以对父类中同名同参数的方法进行覆盖操作(适当改变方法体的内容)
-
应用:重写以后,当创建类对象以后,通过子类对象调用父类中的同名同参数的方法时,实际执行的是子类重写父类的方法
-
重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表){
//方法体(不同)
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
①两个方法的方法名和形参列表必须相同,方法体不同
②子类中的重写的方法权限修饰符不小于父类中的被重写的
tips:子类不能重写父类中的声明为private权限的方法
③返回值类型
- 父类中被重写的方法是返回值类型是void类型,则子类中返回值只能是void 类型
- 父类中被重写的方法是返回值类型是A类型,则子类中可以是A类型或A类型的子类
- 父类中被重写的方法是返回值类型是基本数据类型,则子类中必须是相同的基本数据类型
toString()的使用
- 输出一个对象的引用时,实际上就是调用当前对象的toString()方法
调用父类的构造器:super.toString
?如何实现调用父类的父类的toString方法:
将根父类(父类的父类)中重写的toString方法体放在一个新的方法中,例如:
@override
public String getToString(){
return id + name + age + salary;
}
public String toString(){
return getToString;//return id + name + age + salary;
}
接着在子类的子类中调用 :getToString()方法
instanceof关键字
instanceof关键字的使用
多态性的弊端
有了多态性以后,内存中实际上是加载了子类所特有的属性和方法,但由于变量声明为父类类型(eg:Person p1 = new Man(); ),所以导致编译时,p1对象只能调用父类中声明的属性和方法(包括子类重写的),而不能调用子类所特有的属性和方法
向下转型
目的就是:调用子类特有的属性和方法
向下转型:使用强制类型转换符
Person p1 = new Man();
Man m1 = (Man) p1;//将声明为Person类型的对象强转为Man类型
将子类赋给父类(向上转型:多态)是自动类型提升,而将父类赋给子类则需要强转
-
为了避免向下转型时出现异常,转型前使用instanceof关键字进行判断,若返回true,继续;返回false,不继续向下转型
-
a instanceof A :判断对象a是否是类A的实例,如果是,返回true;如果不是,返回false
-
如果 a instanceof A返回true,a instanceof B 也返回true,
其中B是A的父类
static关键字
有些属性是每个对象都共同拥有,不用给每个对象都定义,static针对的是类,不针对具体的对象,每个对象都公共的拥有同一个static变量
static关键字的使用
- static:静态的
- static可以修饰属性、方法、代码块、内部类
static修饰属性
- static修饰的属性称为静态变量( 或类变量),非static修饰的属性称为实例变量
- 可以通过类直接调用静态属性
- 静态属性随着类的加载而加载
实例变量:我们创建了多个对象,每个对象都独立的拥有一套非静态属性。当通过其中一个对象修改非静态属性时,其他对象的此属性不会被影响
静态变量:我们创建了多个对象,这多个对象共同享有同一个静态变量。通过一个对象修改静态属性时,由于静态属性时公共的,因此其他对象再调用此静态变量时,是被修改过的
栈:局部变量
堆:new出来的结构:对象、数组
方法区:类的加载信息、静态域、常量池
static修饰方法
- 静态方法中不能调用非静态的属性或方法(因为非静态的生命周期不够,静态方法加载完成时还没有非静态的)
- 在静态的方法内,不能使用this、super关键字(this、super都基于当前对象)
- 属性:开发中,当属性可以被多个对象共享,不会随着对象的改变而改变时,可以定义为static
- 方法:工具类中的方法,习惯声明为static
包装类(Wrapper)
包装类介绍
-
希望让八种基本数据类型具有类的特征
-
针对八种基本数据类型定义相应的引用类型—–包装类(封装类)
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
基本数据类型、包装类之间的转换
//基本数据类型--->包装类:调用包装类的构造器
int num = 10;
Integer num = new Integer(num);
//包装类--->基本数据类型:调用xxx包装类的xxxValue()
Integer in1 = new Integer(12);
int in1 = in1.intValue();
Float f1 = new Float(12.3);
float f1 = f1.floatValue();
JDK 5.0新特性:自动装箱与自动拆箱
- 自动装箱:
//自动实现基本数据类型--->包装类(不用调用构造器)
int num2 = 10;
Integer in1 = num2;//自动装箱
- 自动拆箱:
//自动实现包装类--->基本数据类型(不用调用xxx.Value())
Integer in2 = new Integer();
int num3 = in2;//自动拆箱
String类与基本数据类型、包装类之间的转换
//String类型--->基本数据类型、包装类:调用包装类型的 parsexxx(String str)
String str1 = "123";
int num2 = Integer.parseInt(str1);
System.out.println(num + 2); //125
//基本数据类型、包装类--->String类型:调用String重载的 ValueOf(Xxx xxx)
double f1 = 10.1;
String str2 = String.valueOf(f1);
System.out.println(str2 + 2); //10.12
抽象类与抽象方法
abstract
- 抽象的,不实例
- 可以修饰:类、方法
抽象类:
- 此类不能实例化,即不能造此类的对象
- 但一定有构造器,因为此类的子类继承后,还需要调用此父类的构造器
super(name,age);
抽象方法:
- 没有方法体,没有大括号,只有方法的声明
- 由于没有方法体,所以不能用对象调用,因此包含抽象方法的类一定是抽象类
- 若子类重写了父类的所有抽象方法后,才能实例化;若子类没有重写父类的抽象的方法,则子类必须是抽象类
abstract使用注意点:
- 不能用来修饰属性、构造器、代码块等结构
- 不能用来修饰私有方法(子类必须重写抽象父类的方法,但private不能被重写
- 不能用来修饰静态方法、final方法、final类
抽象类的匿名子类:
Person p = new Person(){
//子类的重写的方法
@override
...
};
单例设计模式
让一个类只能存在一个对象实例,并且该类只提供一个获得其对象实例的方法,如果我们要让类在虚拟机中只能产生一个对象,首先要将类的构造器访问权限设置为private,这样就不能用new操作符在类的外部产生类的对象了。由于在类的外部无法得到类的对象,只能调用该类的静态get方法以返回类内部创建的对象。
单例设计模式主要分四步:
1.将类的构造器设置为私有
2.类内部创建类的对象(私有、静态)
3.提供共有的静态方法从类外获取类的对象
4.步骤二中对象也必须声明为静态的
单例设计模式主要有:饿汉式 和 懒汉式
饿汉式:提前创建好对象,安全但占用多余空间
懒汉式:对象在需要用的时候再创建,节省空间但不够安全
异常
java.lang.Throwable:
>java.lang.Error:一般不编写针对代码处理
>java.lang.Exception:异常一般指这个,可以进行异常的处理
Exception分为:
-
编译时异常(checked):
IOException —>FileNotFoundException
ClassNotFoundException
-
运行时异常(unchecked, RuntimeException):
NullPointerException
ArrayIntexOutOfBoundsException
ClassCastException
NumberFormatException
InputMismatchException
ArithmetiException
异常处理
抓抛模型
过程一"抛":程序在正常执行的过程中,一旦出现异常,就会在异常处生成一个异常类的对象,并将此对象抛出。
一旦抛出对象以后,其后的代码不再执行
过程二:“抓”:可以理解为异常的处理方式:
①try—catch—finally
②throws
* //try-catch-finally的使用
*
* try{
* //可能出现异常的代码
*
* }catch(异常类型1 变量名1){
* //处理异常的方式1
* }catch(异常类型2 变量名2){
* //处理异常的方式2
* }catch(异常类型3 变量名3){
* //处理异常的方式3
* }
* ....
* finally{
* //一定会执行的代码
* }
异常处理方式二: throws + 异常类型
-
“throws + 异常类型” 写在方法的声明处。指明此方法执行时,可能会抛出的异常类型
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类常时,就会被抛出。异常代码后续的代码不再执行。
-
体会:try—catch—finally:真正地将异常处理了
throws:只是将异常抛给了方法的调用者,并没有处理
子类重写方法异常的规则:
子类重写的方法中throws的异常类型只能小于等于父类中的异常类型,子类中也可以没有
开发中如何选择?(try—catch—finally or throws )
- 如果父类中被重写的方法没有throws方式处理异常,则子类中也不能用throws,此时如果子类中如果有异常,必须使用try-catch-finally处理。
- 执行的方法A中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。建议让这几个方法使用throws处理,而执行的方法A可以考虑使用try-catch-finally处理
手动抛出异常
throw new 异常对象名();
异常对象需要继承于异常体系,一般写 Exception 或 RuntimeException
也可以自定义,但必须继承于异常体系