1、类和对象
要理解面向对象思想,我们先要知道什么是对象?
《Java编程思想》中提到“万物皆为对象”的概念。它将对象视为一种奇特的变量,它除了可以存储数据之外还可以对它自身进行操作。它能够直接反映现实生活中的事物,例如人、车、小鸟等,将其表示为程序中的对象。每个对象都具有各自的状态特征(也可以称为属性)及行为特征(方法),java就是通过对象之间行为的交互来解决问题的。
面向对象就是把构成问题的事物分解成一个个对象,建立对象不是为了实现一个步骤,而是为了描述某个事物在解决问题中的行为。
类是面向对象中的一个很重要的概念,因为类是很多个具有相同属性和行为特征的对象所抽象出来的,对象是类的一个实例。
类具有三个特性:封装、继承和多态。
面向对象的思想概述
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 执行者就类似是我们封装的方法, 指挥者就是调用封装好的方法的方法
- 面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
类表示一个共性的产物,是一个综合的特征,而对象,是一个个性的产物,是一个个体的特征。 (类似生活中的图纸与实物的概念。)
类必须通过对象才可以使用,对象的所有操作都在类中定义。
类由属性和方法组成:
- 属性:就相当于人的一个个的特征
- 方法:就相当于人的一个个的行为,例如:说话、吃饭、唱歌、睡觉
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:
类名称 对象名称 = new 类名称() ;
如果要想访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:
访问类中的属性: 对象.属性 ;
调用类中的方法: 对象.方法(实际参数列表) ;
- 类必须编写在.java文件中;
- 一个.java文件中,可以存在N个类,但是只能存在一个public修饰的类;
- .java文件的文件名必须与public修饰的类名完全一直;
- 同一个包中不能有重名的类;
- 类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
- 对象是具体的调用者,是具体触发属性改变和行为执行的引用
- “万事万物皆对象”
创建自定义类
•定义类的语法格式:
•[修饰符] class 类名 {….类体…..}
•定义构造器的语法格式:
•[修饰符] 构造器名(形参列表) {……}
•定义属性的语法格式:
•[修饰符] 属性类型 属性名 [= 默认值];
•定义方法的语法格式:
•[修饰符] 方法返回值类型 方法名(形参列表) {….方法体….}
•注意:属性和方法都可以是可以使用static修饰成为类属性、类方法(忽略)
对象的创建和使用
•创建对象的根本途径就是构造器,所以创建对象通过关键字new 加上对应的构造器即可
•类里定义的属性和方法可以通过类或实例来调用
•有static 修饰的访求和属性,既可通过类来调用,也可以通过实例来调用
创建对象语法: 类名 对象名 = new 类名();
使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)
使用“类名.类成员”的方法访问类成员
匿名对象
- 没有对象名称的对象就是匿名对象。 即栈内存中没有名字,而堆内存中有对象。
- 匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,等待被GC回收。
- 只使用一次的对象可以通过匿名对象的方式完成,这一点在以后的开发中将经常使用到。
- 我们经常将匿名对象作为实参传递给一个方法调用。
- 如:new Person().shout();
public static void main(String[] args){
//Math2 m=new Math2();
//int num=m.sum(100,200);
//不通过创建对象名,直接实例对象调用,这就是匿名对象。因为没有对象名指向对象,所以只能调用一次,然后被GC回收。
int num = new Math2().sum(100,200);
System.out.println(num);}
class Math2{
int sum(int x,int y){
return x+y;}}
成员变量和局部变量
成员变量(属性)和局部变量的区别?
成员变量 | 局部变量 | |
声明的位置 | 直接声明在类中 | 方法形参或内部、代码块内、构造器内等 |
修饰符 | private、public、static、final等 | 不能用权限修饰符修饰,可以用final修饰 |
初始化值 | 有默认初始化值 | 没有默认初始化值,必须显式赋值,方可使用 |
内存加载位置 | 堆空间 或 静态域内 | 栈空间 |
从上面程序运行结果来看,不难发现类Field 的作用域比实例Field的作用域更大
成员变量初始化
•成员变量指的是在类范围里定义的变量
•成员变量不用显式初始化,只要定义了一个类属性或实例属性,系统默认进行初始化
成员变量类型 | 初始值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | 0 或写为:’\u0000’(表现为空) |
boolean | false |
引用类型 | null |
局部变量初始化
- 局部变量可分为三种:
形参
方法局部变量
代码块局部变量
- 与成员变量不同的是除了形参外,其他局部变量都必须显式地初始化
- 局部变量仅在方法内有效,当方法执行完成时,局部变量便会自动销毁
变量的使用规则
何时应该使用实例Field?何时应该使用方法局部变量?
这种选择比较困难,如果仅就程序的运行结果来看,大部分时候都可以直接使用实例Field来解决问题,无须使用局部变量。
但实际上这种做法相当错误,因为当我们定义一个成员变量时,成员变量将被放置到堆内存中,成员变量的作用域将扩大到对象存在范围,这种范围的扩大有两个害处:
①增大了变量的生存时间,这将导致更大的内存开销
扩大了变量的作用域,这不利于提高程序的内聚性
隐藏和封装
四种访问权限修饰符
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
对于class的权限修饰只可以用public和default(缺省)。
- public类可以在任意地方被访问。
- default类只可以被同一个包内的类访问。
- protected修饰符我们在继承再讲。
使用权限修饰符
- private 私有的。在同一个类里能被访问
- default 默认的。包访问权限
- protected 受保护的。子类中也能访问
- public 公共的。在任何地方都可以访问
为什么需要封装?封装的作用和含义?
我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
我要开车,…
- 我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。
- 理解封装:封装是面向对象的三大特征之一
封装包含两方面含义:
- 合理隐藏
- 合理暴露
信息的封装和隐藏
- Java中通过将数据声明为私有的(private),再提供公共的(public)
- 方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节;
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
深入构造器
构造方法
构造器通常也叫构造方法、构造函数,构造器在每个项目中几乎无处不在。当你new一个对象时,就会调用构造器。
- 默认构造器
如果没有定义构造器,则会默认一个无参构造器,这就是为什么你定义了一个对象,比如 People,没有定义任何构造器却可以new这个对象,比如 new People() 。如果自定义了构造器,则会覆盖默认构造器。
构造方法的特征
- 它具有与类相同的名称(方法名)
- 它不声明返回值类型。(与声明为void不同)
- 不能被static、final、synchronized、abstract修饰,不能有return语句返回值
构造器的作用:给对象属性进行初始化
- 如:Order o = new Order(); Person p = new Person(“Peter”,15);
- 如同我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造器中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自 动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们 要“洗澡”了。
使用构造器执行初始化
1.构造器最大的用处就是在创建对象时对属性进行初始化。
2.如果程序员没有Java 类提供任何构造器,则系统会为这个类提供一个无参的构造器
3.我们手动编写了带参构造器, 那么默认无参构造器就不会在提供了
思考:构造器是创建Java对象的途径,是不是说构造器完全负责创建Java对象呢?
实际上,当调用构造器时,系统会先为该对象分布内存空间,并为这个对象执行默认初始化,这个对象已经产生了,这些操作在构造器执行之前都已经完成了;只不过这个对象还不能被外部访问,当构造器执行完毕后,会把这个对象返回并赋值给一个引用类型的变量。
- 语法格式:
修饰符 类名 (参数列表) {
初始化语句;}
构造器的重载
- 无参构造器只能对属性进行一些简单的赋值,意义并不大,怎么才能实现动态设置呢?
- 构造器的重载和方法的重载一样,都是方法名相同,形参列表不相同。
- 一旦程序员提供了带参的构造器,系统不再提供无参构造器。
属性赋值过程
我们已经讲到了很多位置都可以对类的属性赋值。现总结这几个位置,并指明赋值的先后顺序。
赋值的位置:
① 默认初始化
② 定义属性的同时赋值
③ 构造器中初始化
④ 通过“对象.属性“或“对象.方法”的方式赋值
赋值的先后顺序:
① -② - ③ - ④
JavaBean
- JavaBean是一种Java语言写成的可重用组件。
- 所谓javaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 私有属性,且有对应的get、set方法
this关键字
(1) this调用本类中的属性,也就是类中的成员变量;
(2) this调用本类中的其他方法;
(3) this调用本类中的其他构造方法,调用时要放在构造方法的首行。
- 在Java中,this关键字比较难理解,它的作用和其词义很接近。
- 它在方法内部使用,本质上就是一个对象,调用这个方法的对象;
- 它在构造器内部使用,表示该构造器正在初始化属性的对象,初始化完成后会返回。
- 可以在方法的返回值直接return this,达到链式调用的效果
- 没有使用 static 修饰的成员变量和方法都必须使用对象来调用。
注意: this不能用在有static修饰的方法中
- this可以调用类的构造器、属性、方法
- 什么时候使用this关键字呢?
我们在方法使用属性或方法的时候,默认会添加this.,可以省略
不能省略:用this来区分成员变量和局部变量。
public void setAge(int age) {
this.age = age;
}
1.在任意方法或构造器内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性。不过,通常我们都习惯省略this。
2.当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加this来表明该变量是类的成员变量
3.this可以作为一个类中构造器相互调用的特殊格式
this在构造器里的使用
注意:
- 可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器
- 明确:构造器中不能通过"this(形参列表)"的方式调用自身构造器
- 如果一个类中声明了n个构造器,则最多有n - 1个构造器中使用了 "this(形参列表)"
- "this(形参列表)"必须声明在类的构造器的首行!
- 在类的一个构造器中,最多只能声明一个"this(形参列表)“
- this调用自己的构造器,实际上就是代码复用,我们在一个构造器里实现的逻辑,在其他的构造器里就没有必要再写一次了, 直接用this调用就可以了。
- this( ) 不能在普通方法中使用,只能写在构造方法中。
关键字package、import
package
- package 打包格式:package 包名;放在程序开始的顶端。
- 包机制的两个方面的保证。
①源文件里要使用package 语句指定包
②class 文件必须放在对应的路径下
- javac -d . HelloWorld.java
- java com.shujia.one.HelloWorld
import
- import 语句可以简化编程,可以导入指定包下某个类或全部类:
- 导入的是包下指定的类。如:
importpackage.subpackage.className;
- 导入的是包下所有的类。如:
importpackage.subpackage.*;
- 如果同名的类存在于多个包下,只能在代码中使用类全名(java.util.Date、java
sql.Date)
import静态导入
•静态导入,导入的是类的静态属性。
•导入的是类下指定的静态属性。如:
importstatic package.subpackage.className.Field;
继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义属性、方法。
- 为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
- 此处的多个类称为子类(派生类),具有公共属性的类称为父类(基类或超类)。可以理解为:“子类 is a 父类”
- 类继承语法规则:
class Subclass extends SuperClass{ }
- 子类继承了父类,就继承了父类的方法和属性。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的属性和方法。
- 在Java中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
•思考: 子类对象到底能不能继承父类的私有属性? 可以继承
Ø子类不能直接访问父类中私有的(private)的成员变量和方法。
Ø子类可以通过调用父类中公开方法达到修改成员变量的效果。
•Java只支持单继承和多层继承,不允许多重继承
- 一个子类只能有一个父类
- 一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2...//error
多重继承 多层继承
重写父类的方法
- 定义:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
- 要求:
1.子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2.子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型(保持一致即可)
3.子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法
4.子类方法抛出的异常不能大于父类被重写方法的异常
- 注意:
- 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写) 。
- 子类与父类中同名同参数的方法同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
super关键字
super调用父类成员
- 在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
- super无法调用父类的private修饰的成员
- 注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类, 父类的父类, 依次向上查找super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
super调用父类的构造器
- 子类中所有的构造器默认都会访问父类中无参数的构造器
- 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
- 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
this和super的区别
this和super都只能在对象内部使用。
this代表当前对象本身,super代表当前对象的父类型特征。
“this.”是一个实例对象内部为了区分实例变量和局部变量。
而“super.”是一个实例对象为了区分是子类的成员还是父类的成员。
构造方法中“this()”和“super()”不能同时出现,也就是“this()”和“super()”都只能出现在构造方法的第一行。
No. | 区别点 | this | super |
1 | 访问属性 | 访问本类中的属性,如果本类没 有此属性则从父类中继续查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没 有此方法则从父类中继续查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造 器的首行 | 调用父类构造器,必须 放在子类构造器的首行 |