面向对象的特征
封装(Encapsulation)
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式.
好处:将变化隔离;便于使用;提高重用性;提高安全性;
封装原则:将不需要对外提供的内容都隐藏起来;把属性都隐藏,提供公共方法对其访问.
之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断等语句.对访问的数据进行操作,提高代码的健壮性.
Java中的权限修饰符:
1.私有权限(private)
private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被private修饰的成员,只能在定义它们的类中使用,在其他类中即使建立了对象也不能直接调用。
2.默认权限(default)
类,数据成员,构造方法,方法成员,都能够使用默认权限,即不写任何关键字。默认权限即同包权限,同包权限的元素只能在定义它们的类中,以及同包的类中被调用。
3.受保护权限(protected)
protected可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被protected修饰的成员,能在定义它们的类中,同包的类中被调用。如果有不同包的类想调用它们,那么这个类必须是定义它们的类的子类。
4.公共权限(public)
public可以修饰类,数据成员,构造方法,方法成员。被public修饰的成员,可以在任何一个类中被调用,不管同包或不同包,是权限最大的一个修饰符。
注意:
1.并不是每个修饰符都可以修饰类(指外部类),只有public和default可以。
2.所有修饰符都可以修饰数据成员,方法成员,构造方法。
3.为了代码安全起见,修饰符不要尽量使用权限大的,而是适用即可。比如,数据成员,如果没有特殊需要,尽可能用private。
4.修饰符修饰的是“被访问”的权限。
常用方式:
将成员变量私有化,对外提供对应的set,get方法对其进行访问.提供对数据访问的安全性.
私有仅仅是封装的一种表现形式.
一个成员变量通常具有两个方法:设置和获取.
封装举例:
class Person{
private String name;//属性私有化,对外隐藏
private int age;
public String getName() {//对外提供设置和获取方法,
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >0 && age <120)//设置值判断,增强程序的健壮性
this.age = age;
else
System.out.println("年龄非法");
}
}
继承(extends ):
1.提高了代码的复用性.
2.让类与类之间产生了关系.有了这个关系,才有了多态的特性.
注意:千万不要为了获取其他类的功能,简化代码而继承;
必须是类与类之间有所属关系才可以继承,所属关系,(是谁的一员).
java语言中:java只支持单继承,不支持多继承.
因为多继承容易带来安全隐患:当多个父类中定义了相同功能,当功能内容不相同时,子类对象不确定要运行哪一个.
但是java保留了这种机制,并用另一种体现形式来完成表示-->多实现.
java支持多层继承.也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系中父类的描述.因为父类中定义的是该体系中的共性功能.
通过了解共性功能,就可以知道该体系的基本功能.
那么这个体系已经基本可以使用了.
在具体调用时,要创建最子类的对象,为什么?
1.因为有可能父类不能创建对象,
2,创建子类对象可以使用更多的功能,包括基本的和特有的.
简单一句话:查阅父类功能,创建子类对象使用功能.
子父类出现后,类成员的特点:
类中的成员:
1.变量.
如果子类中出现了非私有的同名成员变量时,子类要访问本类中的变量,要用this.
子类要访问父类中的同名变量,要用super. super和this的使用几乎一致.
this代表的是本类对象的引用.
super代表的是父类对象的引用.
2.函数.
在子类出现和父类一模一样的函数时;
当子类对象调用该函数,会运行子类函数的内容.
如同父类的函数被覆盖一样.
这种情况是函数的另一个特性:重写(覆盖).
当子类继承了父类,沿袭了父类的功能到子类中.
但是子类具备该功能,但是功能的内容却和父类不一致.
这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容.
覆盖的注意事项:
1.子类函数覆盖父类函数,必须保证子类函数权限大于等于父类函数权限,才可以覆盖,否则编译失败.
2.静态只能覆盖静态.
牢记:
函数重载:只看同名函数的参数列表.
函数重写:子父类方法要一模一样.
3.构造函数.
在对子类对象进行初始化时,父类的构造函数也会运行.
那是因为子类的构造函数默认第一行有一条隐式的语句,super();
super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的.所以子类在对象初始化时,要先访问一下父类中的构造函数.
如果需要访问父类中的指定构造函数.可以通过手动定义super语句的方式来指定.
super语句一定要定义在子类构造函数的第一行.
子类的实例化过程
子类所有的构造函数,默认都会访问父类中空参数的构造函数.
因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数数时,子类必须手动通过super()语句形式来指定要访问父类中的构造函数.
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数.
子类中至少会有一个构造函数会访问父类中的构造函数.
代码体现:
public class Main {
public static void main(String[] args){
Zi z = new Zi("123",10);
z.speak();//调用子类的speak,覆盖了父类的speak
}
}
class Fu{
String name;
int age;
Fu(String name,int age){
this.age = age;
this.name = name;
}
public void speak(){
System.out.println("name="+name+"::age="+age);
}
}
class Zi extends Fu{
//子类继承父类,拥有name和age属性
Zi(String name,int age){
super(name,age);//直接调用父类的构造函数初始化姓名年龄
}
public void speak(){//覆盖父类方法,建立自己特有方法
System.out.println("Zi speak:"+name+"::"+age);
}
}
抽象(abstract):看不懂的
用来修饰类和方法
抽象类的特点:
1.抽象方法一定在抽象类中.
2.抽象方法和抽象类都必须被abstract关键字修饰.
3.抽象类不可以用new创建对象,因为调用抽象类方法没意义.
4.抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用.
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类.
抽象类和一般类没有太大的不同.该怎么样描述事物就如何描述事物.
只不过该事物出现了一些看不懂的东西.
这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体.
通过抽象方法来表示.
抽象类和普通类的区别
抽象类比一般类多了抽象函数.就是在类中可以定义抽象方法.
抽象类不可以实例化.
>>>特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象.
代码体现;
public class RandomTest {
public static void main(String[] args){
Zi z = new Zi("123",10);
z.speak();//调用子类的speak,
}
}
abstract class Fu{//抽象的父类,有一些抽象方法需要子类去实行,不能创建实例
String name;
int age;
Fu(String name,int age){
this.age = age;
this.name = name;
}
public abstract void speak();//子类都要说话,每个子类的实行方式不一样,就定义成抽象的,由子类去具体实现
}
class Zi extends Fu{
//子类继承父类,拥有name和age属性
Zi(String name,int age){
super(name,age);//直接调用父类的构造函数初始化姓名年龄
}
public void speak(){//必须实现父类的抽象方法,如果不实行的话,该子类也必须是抽象类
System.out.println("Zi speak:"+name+"::"+age);
}
}
接口interface
接口:初期理解,可以认为是一个特殊的抽象类.当抽象类中的方法都是抽象的.那么该类可以通过接口的形式来表示.
class用于定义类.interface用于定义接口.
接口在定义时,格式特点:
1.接口中常见定义:常量,抽象方法.
2.接口中的成员都有固定修饰符.
常量:public static final
方法:public abstract
记住:接口中的成员都是public的.
接口是不可以创建对象的,因为有抽象方法.需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化.否则子类是一个抽象类.
接口的特点>>>
接口与类的关系是实现(implements)关系,而且类可以继承一个类的同时实现多个接口.接口可以被类多实现.也是对多继承不支持的转换形式,java支持多实现.
接口是对外暴露的规则,接口是程序的功能扩展.
接口之间是继承关系,接口间可以继承和多继承.
代码体现:
class Person{//定义一个person类,定义一些基本功能,
String name;
int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name = "+name+"::age ="+age);
}
}
interface Smoke{//抽烟接口,扩展功能,需要的就实现
public abstract void smoke();
}
interface Homework{//做作业接口,扩展功能,需要就实现
public abstract void homework();
}
class Student extends Person implements Homework{//student继承person并实现了Homework接口
Student(String name,int age){
super(name, age);
}
public void homework() {//实现homework方法
System.out.println("做作业");
}
}
class Worker extends Person implements Smoke{//worker继承person并实现了smoke接口
Worker(String name,int age){
super(name, age);
}
public void smoke() {//实现smoke方法
System.out.println("抽烟");
}
}
总结:继承的是基础功能(必备功能),接口是扩展功能(可选功能),简单来说,如果是共性必备功能,就定义在父类里面然后子类去继承,如果是扩展可选功能那么就定义在接口里面子类选择性实现.
多态:
可以理解为事物存在的多种体现形式.
1.多态的体现
父类的引用指向了自己的子类对象.
父类的引用也可以接收自己的子类对象,
2.多态的前提
必须是类与类之间有关系,要么继承,要么实现.
通常还有一个前提:存在覆盖.(即子类方法覆盖父类方法)
3,多态的好处
多态的出现大大的提高了程序的扩展性.
4,多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员.
5多态的出现,代码中的特点()
A:在多态中成员函数(非静态)的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过;如果没有,编译失败.
在运行时期:参阅对象所属的类中是否有调用的方法.
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边.
B:在多态中成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类.)
C:在多态中静态成员函数的特点:
无论编译和运行,都参考左边.
代码体现:
public class main{
public static void main(String[] args){
Person p1 = new Student("xue sheng",20);//定义一个person类型指向student对象
p1.show();//调用的是student的show方法
Student stu = (Student)p1;//如果要调用学生的特有方法,就要向下强转,将p1转换成学生类型
stu.homework();
speak(new Worker("worker",50));
}
public static void speak(Person p){//定义一个说话方法,参数类型是person,只要是person的子类都可以,多态的体现
if(p instanceof Person)
p.show();
}
}
class Person{//定义一个person类,定义一些基本功能,
String name;
int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name = "+name+"::age ="+age);
}
}
class Studentsaa extends Person {//student继承person
Studentsaa(String name,int age){
super(name, age);
}
public void homework() {//做作业方法
System.out.println("做作业");
}
}
class Worker extends Person {//worker继承person
Worker(String name,int age){
super(name, age);
}
public void smoke() {//smoke方法
System.out.println("抽烟");
}
}
总结:
多态中,编译的时候看父类中有没有对应的成员,运行的时候看子类有没有覆盖父类的成员.如果覆盖了,就运行子类的成员,没有覆盖就走父类的成员.
运行时,只能调用父类中有的成员函数,子类覆盖了就运行子类的,没有覆盖就运行父类的.
不能调用子类中特有的成员函数,想要调用,就要向下转型成子类类型.
想要在多态中访问子类特有的方法,就需要转型,
类型提升:也叫向上转型.子类对象自动提升为父类类型.
向下转型:强制将父类的引用,转成子类类型
>>>千万不要将父类对象转成子类类型.
能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换.
多态自始至终都是子类对象在做着变化.
instanceof:用来判断所属类型, A instanceof B>>A对象是不是属于B类型.