前言
面向对象是现在高级计算机语言的一个特性,Java、C++、python 等都是面向对象的编程语言。
这里有部分转载的内容。
概念
面向对象 是相对于面向过程 而言的,是基于面向过程演变而来,和面向过程一样,都是一种编程思想。
面向过程
强调的是功能的行为过程,代表语言:C语言。
例子:把大象装进冰箱
1. 打开冰箱。
2. 存储大象。
3. 关上冰箱。
“打开”、”存储”、”关上”都是功能行为,在代码中的直观体现就是函数或者方法,这就是一种面向过程的以功能行为为主体的思想体现。
面向对象
是将功能封装进对象,强调具备了功能的对象。代表语言:Java、C++、Python。
例子:把大象装进冰箱
1. 冰箱打开。
2. 冰箱存储。
3. 冰箱关闭。
可以看到,所有的操作都是以”冰箱”为主体,而不是功能行为。也就是说冰箱自己已经具备”打开”、”存储”、”关上”的行为功能,我们只需要让冰箱执行它具备的功能就可以了。这就是一种面向对象的以执行功能的对象为主体的思想体现。
面向对象的三大特征
- 封装
- 继承
- 多态
类与对象
类:是对现实生活中具体事物的描述
对象:就是这类事物存在的个体。
java中通过类来描述生活中的具体事物,将这个类创建后,就是对象。
类与对象的关系
可以理解为:类就是图纸,汽车就是堆内存中的对象。
对于同一类事物可以抽取它们的共性的内容,定义在类中。如:生活中的汽车,每一台车都有轮胎数和颜色。
那么在通过java描述汽车这类事物时,就可以将这两个共性属性作为类中的属性进行定义。
通过该类建立的每一个汽车实体都具有该属性,并可以有对象特有的属性值。
类的定义
生活中描述事物无非就是描述事物的属性和行为。如:人有身高,体重等属性,有说话,打球等行为。
Java中用类class来描述事物也是如此。
属性:对应类中的成员变量。
行为:对应类中的成员函数。
定义类其实在定义类中的成员(成员变量和成员函数)。
成员变量和局部变量
成员变量:
1. 成员变量定义在类中,在整个类中都可以被访问。
2. 成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。
3. 成员变量有默认初始化值。
局部变量:
1. 局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。
2. 局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。
3. 局部变量没有默认初始化值。
构造方法(函数)
对象的创建及初始化,必须依靠其构造方法。
如果一个类没有定义任何构造方法,则默认有一个无参数的构造方法。
如果类中定义了构造方法,则默认的构造方法失效。
构造方法可以有多个,以重载的形式存在。
构造方法的特点
- 方法名称与类名相同
- 不用定义返回值类型
- 没有返回值
class Person{
private String name ;
private int age ;
//定义一个Person类的构造方法
//构造方法,而且是空参数的
Person(){
//对象创建时,会调用构造方法里面的语句,进行一些初始化操作
//此处表示,对象创建时会打印“person run”
System.out.println("person run");
}
public void speak(){
System.out.println(name + ":" + age);
}
}
class ConsDemo{
public static void main(String[] args){
//构造方法:构建对象时调用的函数
//作用:可以给对象进行初始化
Person p = new Person();
p.speak();
}
}
一般方法与构造方法的区别
构造方法:对象创建时,就会调用与之对应的构造方法,对对象进行初始化,并且只会调用一次。
一般方法:对象创建后,需要方法功能时才去手动调用,可以被调用多此。
this 关键字
this 指代本类对象的引用
当成员变量和局部变量重名,可以用关键字 this 来区分,this 就是所在函数所属对象的引用。
简单说,哪个对象调用了this 所在的方法,this 就代表哪个对象。一般方法调用默认加this。
class Person{
private String name ;
private int age ;
Person(String name){
//通过this区分成员变量和局部变量
this.name = name;
}
Person(String name, int age){
//this也可以用于在构造函数中调用其他构造函数
//this 调用其他构造时,必须写在第一行,因为初始化动作要先执行
this(name);
this.age = age;
}
public void speak(){
System.out.println(name + ":" + age);
}
}
class ConsDemo{
public static void main(String[] args){
Person p1 = new Person("旺财" );
p1.speak();
Person p2 = new Person("小强" ,10);
p2.speak();
}
}
static 关键字
static关键字用于修饰成员变量和成员函数
被修饰后的成员具备以下特点:
- 随着类的加载而加载。
- 优先于对象存在。
- 被所有对象所共享。
- 可以直接被类名调用。
class Person{
//成员变量,实例变量
String name;
//静态变量,类变量
//所有对象共享的属性用static修饰
static String country = "CN";
public void show(){
System.out.println(country + ":" + name);
//等效语句:System.out.println(Person.country + ":" + this.name);
}
}
class StaticDemo{
public static void main(String[] args){
Person p = new Person();
System.out.println(p.country);
//可以用类名直接调用
System.out.println(Person.country);
}
}
成员变量与静态变量的区别
1. 两个变量的生命周期不同
成员变量随着对象的创建而存在,随着对象被回收而释放。
静态变量随着类的加载而存在,随着类的消失而消失。
2. 调用方式不同
成员变量只能被对象调用。
静态变量可以被对象调用,还可以被类名调用。
3. 别名不同
成员变量也称为实例变量。
静态变量也称为类变量。
4. 数据存储位置不同
成员变量存储在堆内存的对象中,所以也叫对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。
静态方法只能访问静态成员,如果访问非静态成员,就会报错
非静态方法既可以访问静态成员,又可以访问非静态成员
封装
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
作用:将变化隔离、便于使用、提高重用性、安全性。
封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。
/*
人:
属性:年龄
行为:说话
*/
class Person{
//private:私有,是一个权限修饰符,用于修饰
//不希望别人直接访问赋值,需要通过私有化把属性进行隐藏
private int age ;
//通过提供set、get公共方法对其访问
public void setAge( int a){
//在set方法内可以对属性的赋值进行限制
if (a > 0 && a < 130){
age = a;
} else
System.out .println("错误的数据" );
}
public int getAge(){
return age ;
}
void speak(){
System.out .println("age = " + age);
}
}
class PersonDemo{
public static void main(String[] args){
Person p = new Person();
//通过其他方式访问
p.setAge(20);
p.speak();
//赋值不合法,set方法就不允许成功赋值
p.setAge(-20);
}
}
继承
通过 extends 关键字让类与类之间产生继承关系。
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为,却无法继承父类中私有的内容。
作用
继承的出现提高了代码的复用性。
继承的出现让类与类之间产生了关系,提供了多态的前提。
class Person{
String name;
int age ;
}
class Student extends Person{
void study(){
System.out.println("student study..." + age);
}
}
class Worker extends Person{
void work(){
System.out.println("worker work..." + age);
}
}
class ExtendDemo{
public static void main(String[] args){
Student s = new Student();
s. name = "zhangsan" ;
s. age = 20;
s.study();
Worker w = new Worker();
w. name = "lisi" ;
w. age = 30;
w.work();
}
}
Java只支持单继承,不支持多继承,但支持多层继承(继承体系)。如:C继承B,B继承A,就会出现继承体系。
特征
变量
子类出现非私有的同名成员变量时,子类访问本类中变量时,用this;子类访问父类中变量时,用super函数(方法)
子类出现和父类一模一样的方法时,当子类调用该方法时,会运行子类方法的内容,因为方法重写(覆盖override)构造函数
子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();
如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。
如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数
final 特点
- 这个关键字是一个修饰符,可以修饰类,方法,变量。
- 被final修饰的类是一个最终类,不可以被继承。
- 被final修饰的方法是一个最终方法,不可以被覆盖。
- 被final修饰的变量是一个常量,只能赋值一次。
抽象类
Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
abstract class Demo{
void show1(){}
void show2(){}
}
特点
- 抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
- 抽象方法只定义方法声明,并不定义方法实现。
- 抽象类不可以被创建对象(实例化)。
- 只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
- 抽象类只能单继承。
抽象类的细节
抽象类中是否有构造函数?
有,用于给子类对象进行初始化。
抽象类中是否可以定义非抽象方法?
可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
抽象关键字abstract和哪些不可以共存?
Final修饰的不能被重写
Private修饰的不能访问
Static修饰的随着类的加载而加载,此时还没有对象
抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
接口
当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口 interface。
interface Demo{
public static final int NUM = 4;
public abstract void show1();
public abstract void show2();
}
/*实现*/
class DemoImpl implements Demo{
public void show1(){}
public void show2(){}
}
class InterfaceDemo{
public static void main(String[] args){
DemoImpl d = new DemoImpl();
System.out.println(d.NUM);
System.out.println(DemoImpl.NUM);
System.out.println(Demo.NUM);
}
}
抽象类和接口的区别
- 抽象类只能被继承,而且只能单继承。 接口需要被实现,而且可以多实现。
- 抽象类中可以定义非抽象方法,子类可以直接继承使用。 接口中都是抽象方法,需要子类去实现。
- 抽象类使用的是 is a 关系。 接口使用的 like a 关系。
- 抽象类的成员修饰符可以自定义。 接口中的成员修饰符是固定的。全都是public的。
多态
定义
某一类事物的多种存在形态。
函数本身就具备多态性,即某一种事物有不同的具体体现。
比如:父类引用或者接口的引用指向了自己的子类对象。
Animal a = new Cat();
好处
提高了程序的扩展性。
弊端
当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。
(前期不能使用后期产生的功能,即访问的局限性)
多态的前提
- 必须要有关系,比如继承、或者实现。
- 通常会有覆盖操作。
class DuoTaiDemo{
public static void main(String[] args){
//自动类型提升,猫对象提升到了动物类型。但是特有功能无法访问,作用就是限制对特有功能的访问。
//专业讲:向上转型,将子类型隐藏。就不能使用子类的特有方法了。
Animal a = new Cat();
a.eat();
//a.catchMouse();//报错
//如果还想用具体动物猫的特有功能。
//你可以将该对象进行向下转型。
Cat c = (Cat)a; //向下转型的目的是为了能够使用子类中的特有方法。
c.eat();
c.catchMouse();
//注意:对于转型,自始至终都是子类对象在做类型的变化。
//Animal a = new Dog();
//Cat c = (Cat)a;//但是类型不能随意转换,否则可能会报出ClassCastException的异常
}
public static void method(Animal a){
a.eat();
}
}
多态的特点
成员函数在多态调用时,编译看左边,运行看右边。
静态成员函数和成员变量,无论编译和运行都看左边。