黑马程序员---java基础之面向对象(一)三大特征(封装,继承,多态)

时间:2023-02-16 17:05:01
------- android培训java培训、期待与您交流! ----------


面向对象的特征

封装(Encapsulation)

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式.

好处:将变化隔离;便于使用;提高重用性;提高安全性;

封装原则:将不需要对外提供的内容都隐藏起来;把属性都隐藏,提供公共方法对其访问.

之所以对外提供访问方式,就是因为可以在访问方式中加入逻辑判断等语句.对访问的数据进行操作,提高代码的健壮性.

Java中的权限修饰符:

1.私有权限(private

private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被private修饰的成员,只能在定义它们的类中使用,在其他类中即使建立了对象也不能直接调用。

2.默认权限(default

类,数据成员,构造方法,方法成员,都能够使用默认权限,即不写任何关键字。默认权限即同包权限,同包权限的元素只能在定义它们的类中,以及同包的类中被调用。

3.受保护权限(protected

protected可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被protected修饰的成员,能在定义它们的类中,同包的类中被调用。如果有不同包的类想调用它们,那么这个类必须是定义它们的类的子类。

4.公共权限(public

public可以修饰类,数据成员,构造方法,方法成员。被public修饰的成员,可以在任何一个类中被调用,不管同包或不同包,是权限最大的一个修饰符。

注意:

1.并不是每个修饰符都可以修饰类(指外部类),只有publicdefault可以。

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. superthis的使用几乎一致.

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类型.


------- android培训java培训、期待与您交流! ----------