1 封装
-
面向对象的三大特征 : 封装, 继承, 多态 。
封装可以从三个层面理解
-
将属性和方法组合在一起(封闭在一起)
-
将属性隐藏起来, 对外提供可以间接操作属性的方法。(提高程序设计安全性)
目前我们都是属性私有化, 并提供与之对应的get和set方法
-
封装应用工具,为其他的程序员提供功能帮助。
属性的get和set方法
-
这是一种封装设计
-
但不是语法要求,是一种约定俗称。
-
一般的要求是,属性私有, 其对应的get和set方法就是在get和set后面连接属性名
class Goods{
private String gname ;
private String kind ;
private int price ;
private int count ;
//可以间接的为kind赋值
public void setKind(String kind){
this.kind = kind ;
}
//可以间接获得kind属性值
public String getKind(){
return kind ;
}
public void setGname(String gname){
this.gname = gname ;
}
public String getGname(){
return gname ;
}
}
class Test{
main(){
Goods g = new Goods();
//g.gname ; 无法直接访问
//g.kind ; 无法直接访问
g.setGname("可乐");// g.gname = "" ;
print( g.getGname() ) ; // print( g.gname ) ; getGname(g)
}
}
特殊的get和set方法
-
get和set方法的写法不一定完全一致。
class A{
private int age ;
public void setAge(String age){
this.age = Integer.parseInt(age) ;
}
}
-
boolean类型的属性,其get方法有些特别,是以is开头
class A{
private boolean flag ;
public void setFlag(boolean flag){
this.flag = flag ;
}
public boolean isFlag(){
return flag ;
}
}
2 继承
需要两个类才能实现继承的效果。 比如:类A 继承 类B
-
A类 称为 子类 , 衍生类,派生类
-
B类 称为 父类,基类,超类
继承的作用
-
子类自动的拥有父类的所有属性和方法 (父类编写,子类不需要再编写) 。 代码复用
目前私有的属性和方法无法访问。
-
多态的基础。
继承语法
-
先定义父类 (单独的类)
-
在定义子类的同时,实现与父类的继承关系
class B{
String name ;
public void t1() {}
}
class A extends B{
//String name ;
//public void t1() {}
}
A a = new A();
a.name = "zs" ;
a.t1();
继承特点
-
继承具有传递性
A extends B extends C
A类中,既有B类的内容,又有C类的内容
-
基础具有不可逆性
A extends B
A类中,有B类的内容
但B类中没有A类的内容。
-
Java只支持单继承 (C++支持多继承)
一个类只能继承一个父类。
但可以连续继承
A extends B extends C
A extends B
A extends C
不可以的
3 子类对象的创建过程
-
不存在父类对象这个概念
class A{
String name ;
public void t1(){}
}
class B extends A{
int age ;
public void t2(){}
}
new A(); //不算父类对象
new B() ;//这就是我们所说的子类对象
-
尽管B对象中的name属性,是从A类中继承而来的
-
但使用过程中,直接通过name名称就可以访问到。
-
可以使用JOL工具来查看内存结果
4 super关键字
可以指定父类的内容 (属性,方法,构造方法)
一般情况下,在子类中,可以默认指定父类的内容。
-
变量访问的顺序: 局部变量->类变量->父类变量->报错
class B{
String name1 ;
}
class A extends B{
String name1 ;
public void t1(){
String name1 ;
print(name) ;
}
}
但有些情况必须使用super来指定父类的内容
-
子类中写了与父类中同名的属性和方法 (重写)
-
在子类的构造方法中,指定要调用的父类的构造方法
在创建子类对象时,不仅仅子类对象的构造方法会被调用,父类的构造方法也会被调用
是在子类构造方法的第一行被调用的。 (先调用子类的构造器,先执行完父类的构造器)
默认会在子类构造方法的第一行,执行super(),表示调用父类的无参构造器
class A{
int a ;
int b ;
public A(int a , int b){
this.a = a ;
this.b = b ;
}
public void A(){}
}
class B extends A{
int c ;
int d ;
public B(){}
public B(int c , int d){}
public B(int a , int b , int c, int d){
//想将a和b交给父类的构造器
//super()
super(a,b);
this.c = c ;
this.d = d ;
}
}
B b = new B(10,20,30,40);
this和super关键字
-
在使用上,this和super特点相似
-
但本质上完全不同
-
this的本质,是一个参数变量,存储的是当前方法所属对应的地址。
-
super的本质,就是一个关键字,用来指定父类的内容。这个指定效果在编译后就没有了。
而this是在运行时有所应用的。
5 多态
事物的多种表现形态。
人有多种表现形态:学生,老师,警察,医生等。
作用:在封装时,可以更灵活的切换具体的执行功能
多态的实现过程:
-
继承
-
重写
-
上转型
5.1 重写
发生在子父类关系中。
在子类中,重新编写与父类相同的属性和方法。 称为方法重写,属性重写
属性重写
-
只需要在子类中定义父类同名的属性即可, 类型可以不同
-
使用上: 类的内部的使用super指定使用的是子类的属性还是父类的属性
类的外部使用时,默认使用的是子类重写的属性
只有在上转型时,可以使用父类的属性。
属性的重写又称为属性的隐藏。
方法重写
-
子类中的方法与父类中的方法名称相同,参数列表相同,执行不同的操作
-
一般情况下 访问权限,返回类型,异常声明 都建议相同。
-
实际上
-
子类重写方法的访问权限要大于等于父类方法的访问权限。
-
子类重写方法的返回类型要小于等于父类方法的返回类型 (基于子父关系)
-
class A{}
class A1 extends A{}
class A2 extends A1{}
class B1{
public A1 t1(){}
}
class B2 extends B1{
public A2 t1(){}
}
子类重写方法的异常声明类型或个数 小于等于父类方法
class E41 extends E4{}
class B1{
public A1 t1()throws E1,E2,E3,E41{}
}
class B2 extends B1{
public A2 t1()throws E4{}
}
-
关于修饰符
-
static关键字修饰的方法不存在重写(概念)
-
final关键字修饰的方法不能被重写
-
abstract关键字修饰的方法必须被重写
-
-
使用上:类的内部可以使用super指定父类的方法
类的外部,只能使用子类重写的方法,无法使用从父类继承的方法,即使上转型也不行
方法的重写又称为方法的覆盖
5.2 上转型
将子类对象的地址存储在父类型的变量中 , 将子类对象当成父类型对象来用(本质还是子类对象)
class A{}
class B extends A{}
B b = new B();
A a = b ;
----------
A a = new B();
-
使用时,因为用的是A类型的变量, 所以以为用的是A类型的对象,所以只能用A类型中才有的属性和方法
-
但实际上, a变量中存储的是B类型的对象, 所以调用的方法都是B类型的方法
-
由于B类型的对象中,既包含B的属性,也包含父类A中的属性, 而使用时又以为使用的是A类型的对象,所以属性调用的就是从A中继承的属性,也就是父类的属性
5.3 下转型
在下转型之前,一定先存在上转型
所谓的下转型,是将之前因为上转型,存储在父类型变量中的子类型对象的地址重新存储在子类型的变量中。类似与强转一样,需要使用()来指定下转的类型。
class A{}
class B extends A{}
A a= new B();
B b = (B)a ;
几种情况:
-
两个类型不存在子父关系,无法下转型(不能强转),此时会出现语法编译错误
User u = new User();
Phone p = (Phone)u ; //语法错误
2.两个类型存在子父关系,但本质类型不相同。 此时语法编译通过,运行出错
class A{}
class A1 extends A{}
class A2 extends A{}
A a = new A1();
A2 a2 = (A)a ; //运行错误 , 类型转化错误
3.两个类型存在子父关系,本质类型也是相同的。此时可以正常编译运行
class A{}
class A1 extends A{}
class A2 extends A{}
A a = new A1();
A1 a1 = (A)a ;
6 final关键字
final关键字可以修饰变量, 方法 和 类
final修饰变量
-
可以是局部变量,也可以是属性变量,也可以是静态变量
-
表示常量,只能被赋值一次,之后不能被改变。
class A{
final static int a = 10;
final int b = 20;
public void t1(final int d){
final int c = 30;
}
}
常量变量和常量值
-
使用final修饰的变量,就成为常量变量
-
在为变量赋值时,=右边那个具体的数值,就称为常量值 10 , 20 , "abc" , 'a' , true
final修饰方法
称为最终方法, 不能被重写
class A{
public void t1(){}
public final void t2(){}
}
class B extends A{
public void t1(){}
public void t2(){} //错误
}
final修饰类
称为最终类,不能被继承
7 Object类
是所有类的终极父类。
无论是jdk自带的类或数组,还是我们自定义的类,最终都会继承Object这个父类
而且不需要写出来。
Object类中的方法所有的类都具备
-
getClass() 后面反射时会讲
-
clone() 后面讲接口和异常时会讲
-
hashCode() 后面讲集合的时候会讲
-
finalize() 后面讲异常时会讲
-
wait() , wait(long) , wait(...) , notify() , notifyAll() 后面讲线程会讲
-
toString() 返回一个字符串,用来描述当前对象
-
equals() 用来比较当前对象与另一个对象是否相等
String toString()方法
-
返回一个字符串,代表当前对象的一个文字描述
-
默认toString方法返回的内容包括对象的类型及对象的地址
-
在打印对象时,会默认打印对象的toString方法的返回值。
boolean equals(Object obj) 方法
-
equals用来比较两个对象是否相等。通过源码可知, 使用的是 == 比较。
-
== 可以比较基本类型 , 也可以比较引用类型。 比较基本类型比的是值,比较引用类型比较的是地址
-
equals只能比较引用类型,默认比较的是地址,但可以通过重写,改成比较内容. 像String就重写了equals方法,改为比较内容了
-
-
比较哪两个对象是否相等呢?
-
一个是this这个对象
-
还有一个是以参数传递过来的对象 obj
-
obj的本质也是当前类型的对象。 目的就是比较两个相同类型的对象是否相等
-
可以通过强转,将obj还原成最开始的类型, 并完成this与obj的属性内容比较
-
class G{
String name = "zs" ;
int age = 18 ;
public String toString(){
return "my name is "+ name + ", my age is " + age ;
}
public boolean equals(Object obj){
if( this == obj){
return true ;
}
G g = (G)obj ;
if(this.name.equals(g.name) && this.age == g.age){
return true ;
}else{
return false ;
}
}
}