Java面向对象基础
面向对象基础
一、什么是面向对象
把大象放进冰箱分为三步
1、打开冰箱
2、把大象放进冰箱
3、关上冰箱
假设我想把大象放进冰箱,使用面向过程的思想,我首先需要先思考如何打开冰箱,接着如何把大象那么大的动物放进冰箱(切片还是怎么放),最后是关上冰箱门,一步一步实现。
如果是使用面向对象思想,直接把冰箱看成一个对象,直接操作对象就行,当需要装大象时,我们就可以直接通过操控对象就可以了。
二、类和对象的关系
(1)类:类是对像是生活中事物的描述
(2)对象:对象就是这类事物实实在在存在的个体
在我们现实生活中的对象有张三、李四,宝马,奔驰,牡丹,菊花,想要描述这些对象,就得对它们进行共性的提取,比如可以把张三、李四抽象成一个类,把他们的共性封装在类中他们的共性就是他们都有姓名、性别、年龄等共性,同样宝马,奔驰,牡丹、菊花也有他们的共性,都可以抽取出来。
Java 描述事物是通过类的方式实现,而类是通过new操作符所产生的实体来实现,而这个实体在堆内存中在映射到Java
中去。
Java代码:
public class Car {
//描述颜色
String color="red";
//描述轮胎数
int tyre_num = 4;
//运行行为
void run(){
System.out.println("color:"+color+"; 轮胎数:"+tyre_num );
}
}
(3)类对象在内存中的图示解析
生产一辆车
如上图,首先在栈内存中有一个bmw车实例;然后在堆内存中new Car(),默认初始化color为null,tyre_num为0;再将color设置显示初始化值为”white”,将tyre_num设置显示初始化值为4;将new Car()产生的地址值0x1103赋给栈内存,再由栈指向堆中的0x1103的地址。
2、生产两辆车
如上图所示,建立多个对象的过程:首先在栈中初始化bmw,通过new Car()方式在堆中建立Car对象,初始化color为null,tyre_num为0,再将color赋值为”white”,将tyre_num赋值为4。将new Car()产生的地址值0x1129赋给栈内存,再由栈指向堆中的0x1129的地址。在栈中初始化audi,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color赋值为”red”,将tyre_num赋值为4。将new Car()产生的地址值0x1213赋给栈内存,再由栈指向堆中的0x1213的地址。
(4)匿名对象
匿名对象是对象的简化形式。有两种使用情况:当对对象方法仅进行一次调用时;匿名对象可以作为实际参数进行传递。
例如:
Car c = new Car();
c.tyre_num= 5;
可简化为:
new Car().tyre_num= 5;
new Car().color = “red”;
new Car().run();
(5)匿名对象和非匿名对象区别
匿名对象在栈中无数据。使用匿名对象时,当new Car().tyre_num= 4;执行完后创建的对象就成为垃圾;new Car().color = “red”执行完后,此对象也会成为垃圾,因此匿名对象调用属性是没有意义的。但是,new Car().run()有意义。
综上所述,匿名对象调用属性无意义,调用方法有意义。
匿名对象的两种使用方式:
(1) 匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样比较简化;但是,如果对一个对象进行多个成员调用,必须给这个对象起名字。例如:
Car c = new Car();
c.run();
c.num=4;
new Car().run();
(2)匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。如果有这么一个需求:汽车修配厂,对汽车进行改装,将来的车都改成黑色且只有三个轮胎。
main(){
Car car = new Car();
show(car);
}
public static void show(Car car){
c.tyre_num = 3;
c.color = “black”;
c.run();
}
三、封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。(在使用对象时,没有必要知道对象内容是如何完成对应功能的,我们只需要指挥对象去执行即可)。
封装的好处:
将变化隔离(内部功能细节变化不影响使用);便于使用(不用了解内部的具体实现);提高重用性;提高安全性(只对外暴露一些简单的内容供使用)。
封装的原则:
将不需要对外提供的内容都隐藏起来;把属性都隐藏起来,只提供公共方法对其访问。
函数本身就是java代码中的最简单的封装体。类也是封装体,类中方法不一定全部暴露出去,可通过权限修饰符进行隐藏。包也是封装体,如框架,拿来即用。
四、构造函数
(1)构造函数特点:
函数名与类名相同
不用定义返回值类型
不可以写return语句
(2)构造函数的作用:给对象进行初始化。
构造函数对象一建立就会调用与之对应的构造函数,可用于给对象进行初始化。当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数,当自己定义了构造函数后,默认的空构造函数就不存在了。注意:默认构造函数的特点;多个构造函数是以重载的形式存在的。
(3)构造代码块:
在构造代码块中,对象一建立就立即运行,而且优先于构造函数执行。构造代码块中定义的是不同对象具有共性的初始化内容。构造代码块和构造函数的区别:构造代码块是给所有对象进行统一初始化;而构造函数是给对应的对象初始化。
五、this关键字
this关键字代表自身,在程序中主要的用途
1.用this在自身的构造方法内部调用其他的构造方法
2.用this代表自身类的对象
直接使用this
使用this引用成员变量
使用this调用成员方法
this的值是当前对象的引用,只能用它来调用属于当前对象的方法或者使用this引用成员变量和局部变量重名
看一个例子:
public Class Student{
String name;//定义一个成员变量name
private void SetName(String name){//定义一个参数(局部变量)name
this.name=name;//将局部变量的值传递给成员变量
}
}
应用一:引用成员变量
应用二:调用类的构造方法
public class Student { //定义一个类,类的名字为student。
public Student() { //定义一个方法,名字与类相同故为构造方法
this(“Hello!”);
}
public Student(String name) { //定义一个带形式参数的构造方法
}
}
this关键字除了可以调用成员变量之外,还可以调用构造方法。
应用三:返回对象的值
this关键字除了可以引用变量或者成员方法之外,还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this,来返回某个类的引用。此时这个this关键字就代表类的名称。如代码在上面student类中,那么代码代表的含义就是return student。可见,这个this关键字除了可以引用变量或者成员方法之外,还可以作为类的返回值,这才是this关键字最引人注意的地方。
为什么静态成员、静态方法中不能用this和super关键字
1. 在静态方法中是不能使用this预定义对象引用的,即使其后边所操作的也是静态成员也不行.
因为this代表的是调用这个函数的对象的引用,而静态方法是属于类的,不属于对象,静态方法成功加载后,对象还不一定存在
- super的用法:
(1).super的用法跟this类似,this代表对本类对象的引用,指向本类已经创建的对象;而super代表对父类对象的引用,指向父类对象;
(2).静态优先于对象存在;
(3).由上面的1.和2.知:
因为静态优先于对象存在,所以方法被静态修饰之后方法先存在,而方法里面要用到super指向的父类对象,但是所需的父类引用对象晚于该方法出现,也就是super所指向的对象没有,当然就会出错。综上,静态方法中不可以出现super关键字。
- 明白对象和类的区别。
this和super是属于对象范畴的东西,而静态方法是属于类范畴的东西
所有的成员方法,都有一个默认的的参数this(即使是无参的方法),只要是成员方法,编译器就会给你加上this这个参数如:
Class A中
void method1(){}实际上是这样的--------> void method1(A this)
void method2(int x){}实际上是这样的--------> void method2(A this, intx)
而静态方法与对象无关,根本不能把对象的引用传到方法中,所以不能用this
4.在一个类中定义一个方法为static,则为静态方法。
那就是说,无需本类的对象即可调用此方法,调用一个静态方法就是“类名.方法名”既然"无需本类的对象即可调用静态方法",而this和super关键字都是用于本类对象的-----调用静态方法无需本类的对象这句话很清楚表明:静态方法中不能用this和super关键字
5. 静态方法是存放在内存中的数据段里
6. this和super调用的是堆空间里的应用对象不能调用数据段区域里的数据
因此静态方法中不能用this和super关键字
7. 静态方法和静态类不属于单个对象,而是类的所有对象共享使用
而this代表当前对象
8. 东西只属于类,不属于任何对象,所以不能用this和super。
六、static关键字
在一个类中,主要的组成就是属性和方法(分为构造方法与普通方法两种),而每一个对象都分别拥有各自的属性内容(不同对象的属性保存在不同的堆内存中),如果现在类中的某个属性希望定义为公共属性(所有对象都可以使用的属性)则可以在声明属性前加上static关键字。
static关键字说明
在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,当然也可以修饰代码块。
Java把内存分为栈内存和堆内存,其中栈内存用来存放一些基本类型的变量、数组和对象的引用,堆内存主要存放一些对象。
在JVM加载一个类的时候,若该类存在static修饰的成员变量和成员方法,则会为这些成员变量和成员方法在固定的位置开辟一个固定大小的内存区域(只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们),有了这些“固定”的特性,那么JVM就可以非常方便地访问他们。
如果静态的成员变量和成员方法不出作用域的话,它们的句柄都会保持不变。同时static所蕴含“静态”的概念表示着它是不可恢复的,即在那个地方,你修改了,他是不会变回原样的,你清理了,他就不会回来了。
同时被static修饰的成员变量和成员方法是独立于该类的,它不依赖于某个特定的实例变量,也就是说它被该类的所有实例共享。所有实例的引用都指向同一个地方,任何一个实例对其的修改都会导致其他实例的变化。
八、继承
1、什么是继承
2、为什么使用继承
从已有的类派生出新的类,称为继承。
在不同的类中也可能会有共同的特征和动作,可以把这些共同的特征和动作放在一个类中,让其它类共享。
因此可以定义一个通用类,然后将其扩展为其它多个特定类,这些特定类继承通用类中的特征和动作。
继承是 Java 中实现软件重用的重要手段,避免重复,易于维护,易于理解。
3、父类和子类如果类 B 从类 A 派生,或者说类 B 扩展自类 A,或者说类 B 继承类 A,则称类 A 为"父类",也称为超类、基类;称类 B 为"子类",也称为次类、扩展类、派生类。子类从它的父类中继承可访问的数据域和方法,也可以添加新的数据域和新的方法。
定义继承的语法:
修饰符 class 子类名 extends 父类名
例如:Shape 类是父类,其子类可以有 Circle 类、Rectangle 类、Triangle 类,等等。
继承的注意点:
(1)子类不是父类的子集,子类一般比父类包含更多的数据域和方法。
父类中的 private 数据域在子类中是不可见的,因此在子类中不能直接使用它们。
(2)继承是为"是一个"的关系建模的,父类和其子类间必须存在"是一个"的关系,否则不能用继承。
(3) 但也并不是所有"是一个"的关系都应该用继承。例如,正方形是一个矩形,但不能让 Square 类来继承 Rectangle 类,因为正方形不能从矩形扩展得到任何东西。正确的继承关系是 Square 类继承 Shape 类
(4) Java 只允许单一继承(即一个子类只能有一个直接父类),C++ 可以多继承(即一个子类有多个直接父类)。
九、多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
多态的优点
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
多态存在的三个必要条件
1.继承
2.重写
3.父类引用指向子类对象
比如:
Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
虚函数
虚函数的存在是为了多态。Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
重写
我们将介绍在 Java 中,当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字 super。
多态的实现方式
方式一:重写:
这个内容已经在上一章节详细讲过,就不再阐述,详细可访问:Java 重写(Override)与重载(Overload)。
方式二:接口
1. 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。
2. java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。具体可以看 java接口 这一章节的内容。
方式三:抽象类和抽象方法
十、异常
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
异常发生的原因有很多,通常包含以下几大类:
1.用户输入了非法数据。
2.要打开的文件不存在。
3.网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
*(1)检查性异常:*最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
(2) 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
(3) 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类的层次
所有的异常类是从 java.lang.Exception 类继承的子类
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
总结
本文介绍了Java的基础知识,仅做入门使用,用于本人的复习使用,大多数内容来自于网络,记录一下,与君共享