Java之类与对象、访问权限
1对象的概念
可以把对象理解为一个物体。对象可以是看得见和看不见的,存在和不存在。一切都是对象。
对象具有如下的两个特征:
1、属性:对象区别于其他对象的表现。
2、行为:事物能够做什么,有什么样的能力,表现为什么样的动作。
属性多表现为名词,行为多表现为动词。
2类的概念
1、类是现实世界事物的抽象,是具有相同属性与行为的对象的集合。
2、类与对象的关系:
类是设计蓝图,对象是蓝图设计的产物。
类是对象的抽象,对象是类的具体的表现形式。
3、现实与程序的映射
类是由相同属性与行为的对象组成的集合。体现为类别。
现实世界中事物的属性映射到类中,就表现为类的成员变量。
现实世界中事物的行为映射到类中,就表现为类的方法。
3类和对象的关系
4消息
与对象进行交互,实际上就是给对象发送消息。
对象之间通过消息传递机制建立联系。
消息的组成
消息的目的对象,要执行的方法的名称,方法所需要的参数。
要使用消息,消息就必须有目标对象,也就是消息发送给谁,谁使用这个消息。在编程语言中体现为类或者对象。
由于每个对象可能会提供多种消息,所以必须确定目标对象到底是消费哪个消息。在编程语言中就体现为不同的方法。
确定了消息以后,每条消息还可以提供细节的控制。在编程语言中就表现为方法的参数。
消息的性质
1、同一对象可以接收不同形式的多个消息,产生不同的响应。
2、同一个消息可以发送给不同的对象,所做出的响应可以截然不同。
3、发送方不需要知道接收方是如何对请求予以响应的。
5面向对象的三大特征
封装性
封装就是隐藏具体的实现细节,只提供给外界调用的接口。这样,底层细节的改变,不会对外界造成影响,只要提供给外界的接口不变即可。
继承性
当两个类存在一般与特殊的关系时,我们就称特殊的类继承了一般的类。
特殊类具有一般类的一切特征,并且还具有自己专门的特征。
如果类型A是一种特殊的类型B(特殊与一般的关系),则应该让类型A继承类型B。
如果类型A是类型B的一部分(包含与被包含的关系),则应该用类型B去组合类型A。
C++等语言支持多重继承,但是Java语言在类上只支持单重继承。
举一个例子:菱形是一种特殊的平行四边形,矩形也是一种特殊的平行四边形,所以让菱形和矩形分别继承平行四边形是没有问题的,这都是单重继承。而正方形既是一种特殊的菱形,也是一种特殊的矩形。这个时候,正方形就同时继承了菱形和矩形。虽然现实世界中有多重继承的例子,并且一些语言也支持多重继承。但是这在java中是不允许的。Java中的类不能同时继承两个或者更多的类。
继承的作用
1、可以更好地进行抽象与分类。
2、减少代码和数据的冗余,实现代码的复用。
3、提供程序的可维护性。
多态性
多态性就是通过同一个引用,调用不同的方法,在运行时可以表现为不同的形态。多态性是表现在继承的基础之上的。也就是说,如果要实现多态,则必须要首先实现继承。
6类的声明
声明类,同时声明类的特征(属性)、行为(方法)。类的声明包括两部分:类声明和类体。格式如下:
[类修饰符] class 类名{ 类体 } |
说明:在类中,属性是用过成员变量来体现的,而行为是使用方法(函数)来体现的。
7成员变量
直接声明在类中的变量,称为成员变量(也称为字段或者域)。
成员变量的作用域是整个类。
声明在方法内部或者块中的变量称为局部变量。
局部变量的作用域是整个方法或者整个块。
成员变量和成员变量不能重名,但是成员变量可以和局部变量重名。
如果局部变量和成员变量同名,在局部变量的作用域内,局部变量将会隐藏(遮蔽)同名的成员变量。如果想访问被局部变量隐藏的实例成员变量就应该使用this。如果想在静态方法中访问被静态方法的参数所遮蔽的静态成员变量就应该使用类名来访问。
8方法的声明
格式:
[方法修饰符] 返回类型 方法名(参数列表){ 方法体 } |
参数列表的格式:
参数类型1 参数名1,参数类型2 参数名2,...参数类型n 参数名n |
说明:返回类型可以是基本数据类型,也可以是引用类型。此时需要使用return返回一个值。但返回类型是void的时候,方法没有返回值。
含有参数的方法:
在方法声明处的参数称为形式参数(简称形参),
在方法调用处的参数称为实际参数(简称实参),
在方法调用时,实际参数会赋值给对应的形式参数。
方法中声明的参数是局部变量,在方法体内有效。
方法的返回类型
方法可以给调用端返回一个具体的值,这个值通过return来返回。
当方法返回具体值时,可以近似看成是返回值替换掉了方法的调用。
注意:方法只能有一个return。也就是方法只能有一个返回值,当需要返回多个值时,可以使用数组或者集合,即返回一个数组类型或者集合类型。
public claa F{ Boolean X(int y){ If((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) return true; return false; } } |
9对象的创建和使用
创建一个类的对象实际上就是创建一个类的实例。
创建一个类的对象使用关键字new来实现。
创建对象后,可以使用“引用.成员”来访问类中声明的成员。
声明引用类型的变量与创建的对象是两个完全不同的概念,如果仅仅声明了引用变量,而没有创建对象,则无法通过引用来访问类的成员。
10 变量的默认值
如果没有显式地为成员变量初始化,则成员变量的初始值如下:
1、如果没有为局部变量显式的初始化,则局部变量不会有任何的默认值,尝试使用未初始化的局部变量将会引发编译错误。仅声明而不适用没有初始化的局部变量不会产生错误。
2、数组类型也是通过new来创建的,所以创建的数组类型也是对象。从数组元素默认值的角度讲,数组元素可以近似的认为是类中的成员变量,并具有默认值,并且与成员变量的默认值一致。
11 null
null是引用类型的字面常量,可以赋值给任意的引用类型,表示没有指向任何有效的对象,即引用变量本身没有存放任何有效对象的起始地址。
如果使用值为null的引用去访问对象的成员,可以通过编译,但是会在运行时产生NullPointerException异常。
12 对象的内存分配
1、对象与数组一样,是使用new来创建的(实际上数组也是对象),对于使用new创建的对象,会分配在堆内存中。
2、局部变量分配在栈内存中。栈内存随方法的执行而创建,当方法结束的时候,栈内存也会随之销毁。
3、每创建一个对象,就会在堆内存中分配一块独立的空间。这意味着,每个对象都有自己的成员变量(实例),彼此之间不受影响。
类中声明的方法,是由所有对象所共享的。
13 参数传递
Java中的参数传递可以分为两种情况:
1、参数的类型是基本数据类型的时候
2、参数的类型是引用类型的时候
不管参数的类型是什么,参数传递永远是值传递。不过,当参数是基本数据类型的时候,形参的改变无法反作用于实参。当参数是引用类型的时候,可以通过更改引用指向对象的数据,但尽管如此,依然无法改变引用本身。
14 面向过程和面向对象
在面向过程的编程语言中,是由函数组成的,功能通过调用函数来实现。而面向对象编程语言中,以对象为主,动作是由对象发出的。
面向对象编程语言可以体现为主谓宾结构,更符合现实中的设计。
面向对象编程语言可以体现出动作的发出者与承受者。
15 方法重载
单独的一个多音字,我们无法确定这个字的读音。但是,如果把这个字放在具体的语言环境中,我们就可以明确它的具体读音。
我们把方法名字相同,参数列表(形式参数)不同的两个或者多个方法称为重载方法。而这种现象就称为方法重载。
16重载方法的语言环境
多个重载方法的区分条件为参数列表不同。
构成方法重载的要求是:
1、方法的名字相同
2、方法的参数列表不同
参数列表不同具体体现为以下两点:
1、参数列表的个数不同。
2、参数列表所对应的参数类型不同。
编译器需要上下文环境来决定调用哪一个重载方法,参数列表的不同就是编译器的上下文环境。
示例
void m(){} void m(int x){} |
void m(int x){} void m(){float f} void m(String s){} |
注意:参数的名字相同和方法的返回类型不同不能作为重载方法的区分条件。
方法的返回类型不能作为重载方法的原因是我们在调用方法的时候可以忽略返回值。
17方法重载的优点
1、在声明方法的时候,可以把功能相似的方法定义为重载方法,而无需为每一个方法取不同的名字。
2、在调用方法的时候,只需要通过一个方法名,即可实现不同方法的调用,进而实现相似的功能。
18重载方法的设计原则
我们应该把功能相似的方法设计为重载方法,而不要将功能不相关的方法声明为重载方法。
19 重载方法的调用原则
1、因为重载方法功能相似,所以各个方法中很有可能实现完全相同,这就会造成代码的重复。因此,我们应该考虑使用一个重载方法调用另外一个重载方法,而不是每个方法都含有相同的代码。
2、重载方法调用的原则为:
参数个数不同时:参数少的调用参数多的。
参数类型不同时:参数简单的调用参数复杂的。
但是这并不是绝对的原则,在某些情况下,可能是参数多的调用参数少的重载方法。
20 构造器
我们创建对象的时候,需要对类中的成员变量进行赋值(初始化)。虽然可以在创建对象后进行相关的赋值操作,但创建对象与初始化是分开进行的。
我们是可以在创建对象的时候,就初始化类的实例变量。
构造器,在类中声明,在是因为new创建对象的时候,会自动调用构造器。构造器也称为构造方法。
说明:
1在构造器中对类的实例变量进行初始化是很合适的。
2创建和初始化一体化,无需在创建对象后,再次对成员变量进行赋值操作。
构造器的名字与类名相同,并且没有返回类型。连void也没有。访问权限和参数列表与普通方法相同,没有限制。
说明:
1、没有返回类型。与返回类型是void是两个不同的概念。
2、尽管构造器的外观上与方法非常类似,但是需要注意的是,构造器不是方法,也不是类的成员。
21 构造器的声明
构造器的名字与类名相同,并且没有返回类型。访问权限和参数列表与普通的方法相同,没有限制。
构造器会在使用new关键字创建对象的时候自动执行,每创建一个对象,构造器都会执行一次。
22默认的构造器
1、如果在类中没有显式的声明构造器,则编译器会自动为该类创建一个无参的构造器。这个构造器通常称为默认的构造器。
2、默认的构造器访问权限与类的访问权限相同,参数列表为空。
3、如果类中预警显式地声明了构造器,则编译器将不再为该类生成默认的构造器。
4、不论是显式声明的构造器是否具有参数,默认的构造器都将不复存在。
23构造器的重载
1、可以在类中定义多个构造器,以实现多种初始化的方式。
2、由于构造器与类名相同,所以如果类中存在多个构造器,则多个构造器必然构成重载。
3、与重载方法相同,构造器重载的时候,应该有限考虑使用一个构造器,调用另外一个构造器来实现,调用的原则与方法重载的调用原则相同。
24构造器的调用
1、在一个类的多个构造器中,可能会出现一些复杂的曹傲,为了提高代码的可重用性,java语言允许在一个构造器中,使用this语句来调用另一个构造器。
2、构造器调用的原则和重载方法的调用原则类似。
使用this语句调用构造器的时候,必须遵守以下的语法规则:
1、假如在一个构造器中使用了this语句,那么它必须作为构造器的第一条语句。(语句顺序)
2、只能在一个构造器中用this来调用类的其他构造器,而不能在构造器外使用this调用类的其他构造器。(调用位置)
3、只能使用this调用其他的构造器,而不能通过构造器名来直接调用构造器。(调用方式)
25 this关键字
this关键字的使用:
1、在任何的实例方法中,this指向当前对象。(谁调用的该方法,谁就是当前对象)
2、可以用来调用构造器。
无论何时,我们都可以通过this访问成员变量。只是,如果成员变量没有被局部变量所遮蔽,我们可以省略this。
当方法(构造器)在调用时,会隐式传递一个参数(作为参数列表中的最后一个参数,该参数就是当前对象的引用,也就是this。
说明:
1、在构造器或者实例方法中,如果方法(构造器)参数和成员变量同名,则需要使用this来访问被隐藏的成员变量。
2、不能在静态成员中使用this,因为静态成员不是对象实例的一部分。
26实例变量的初始化
实例变量(没有使用static修饰)初始化可以采用如下的三种方式:
1、声明时初始化
2、使用初始化块
3、在构造器中初始化
初始化块(实例)就是一对{}。
初始化块声明在类的内部(不是方法中,有别于语句块)
在创建对象时,初始化块会自动执行。
每创建一个对象,初始化块都会执行一次。
三种初始化方式的执行顺序:
变量声明处初始化与初始化块会在构造器之前得到执行。
变量声明处初始化与初始化块根据在类中声明的顺序执行,谁先声明,谁先执行。(?)
27可变参数方法
从JDK1.5开始,新增了一种新的形参类型--可变参数。使用...来表示,这使得我们都在调用方法的时候,可以根据需要传递0个或者多个实际参数。
main方法的args参数实际上就是可变参数。
可变参数必须作为参数列表的最后一个参数。这意味着,一个方法至多只能有一个参数是可变参数。
当可变参数参与重载的时候,可变参数的优先级低于非可变参数。这是为了兼容以前的程序。
可变参数的底层就是一个数组,因此,相同类型的可变参数与数组不能构成重载。
1、public void f(int x) {} 2、public void f(int x, int y){} 3、public void f(int... x) {} 4、public void f(int[] x) {} |
如上的4个方法,1、2、3构成了重载方法。在使用f(5)进行调用的时候, 方法3也是符合条件的,但是,程序会调用方法1,而不是方法3。只有程序中没有方法1的时候,才会调用方法3。方法3和方法4不能构成重载。
28包
包(package)类似于操作系统上的文件夹,提供一个独立的命名空间。
使用包具有如下的作用:
1、包可以将相关的类组织在一起。
2、包可以提供独立的命名空间,解决命名冲突。
3、包可以提供访问权限的控制。
29包的声明方式
package name; |
1、package为声明包的关键字。
2、name为指定的包名,可以分层次,不同层次使用点号“.”分隔。
3、package语句作为Java源文件的第一行语句,执行该类所在的包。一个源文件只能有一条包的声明。
如果没有声明包,则默认会将类存放在无名包中。
1、建议将类放在包中,而不要使用无名包。
2、按照惯例,包名使用小写字母组成。
3、建议包名采用“域名的倒写.项目名称.模块名”的形式,以确保包名的唯一性,例如:com.bnis.business.action。
30包的引入
在Java程序中,不能直接访问包外的类,需要访问包外的类时,可以使用两种方式:
1、使用类的全限定名(包含包名),如:
Ch3.src.TestPackage test=new ch3.src.TestPackage();
2、使用import关键字引入包,然后使用。
improt some.TestPackage; ... TestPackage test=new TestPackage(); |
说明:package需要在import前使用,import需要在类声明之前使用。
31包的声明
声明的方式
import pkg1[pkg2].(classname|*); |
1、使用点号(.)来隔离多级包名。
2、classname表示明确导入。
3、*表示按需导入。
4、一个Java源文件中可以有多条import语句。
说明:java.lang包会被自动导入。
32在import中使用*
1、*导入包中所有的类,这回增加编译的时间,但是不会增加运行时间。因为类的确定是在编译期间完成的。
2、如果引入的两个包中有同名的类,当我们试图运行其中的一个,则会导致一个编译时错误。
3、*会导入包中所有的类,但不会级联的导入其子包(如果存在的话)中的类。
33 improt static
1、可以使用import static来导入指定类中的静态成员。当使用import static导入类中声明的静态成员后,当前类中,就可以直接使用导入的静态成员,就好像当前类中自己声明的一样。
2、improt static也分为明确导入和按需导入。
3、使用import static导入类的静态成员的时候,类需要使用全限定名来指定。
34 访问修饰符
信息隐藏是OOP(Object Oriented Programming,面向对象编程)最重要的功能之一,也是使用访问修饰符的原因。
信息隐藏的原因包括:
1、对任何实现细节所作的更改不会影响使用该类的代码
2、防止用户意外删除数据
3、此类易于使用
访问修饰符用来声明访问权限,访问权限由高到低为:
1、public 公有权限,全部对外开放。
2、protected保护权限,对本包开放,对包外的子类开放。
3、不加修饰符限定,默认权限,对本包开放。
4、private私有权限,仅对本类开放。
说明:
访问修饰符可以修饰类(接口),方法,成员变量,构造器。
相关访问:
1、顶层类可以声明为public或者默认访问权限。内部类可以是任意访问权限。
2、方法,成员变量,构造器可以是任意访问权限。
3、局部变量不能使用访问修饰符。
4、在类的内部,可以访问任意权限的成员和任意权限的构造器。因为在创建对象的时候,会自动调用构造器,因此,如果构造器没有权限访问,就不能正常对对象进行初始化,因为也就不能创建对象。
35 static
static可以修饰类(内部类),方法,成员变量。
1、当static修饰成员类时,该类为静态成员类。
2、当static修饰方法时,该方法为静态方法。无sttaic修饰的方法为实例方法。
3、当static修饰成员变量时,该变量为静态成员变量。无static修饰的成员变量称为实例成员变量。
4、static不能修饰局部变量。
36 静态成员变量和实例成员变量
1、静态成员变量为类所有,而实例成员变量为对象所有。
2、静态成员变量在内存中仅分配一份空间,由所有的对象共享。而实例成员变量是每创建一个对象,就会分配一份空间。
3、当一个对象修改了静态成员变量的值,其他对象访问静态成员变量的时候,得到的将是修改以后的结果。而实例成员变量为对象所有,各个对象之间互不干扰。
4、静态成员变量会在第一次使用类之前得到初始化,而实例成员变量在创建对象的时候才会初始化。
5、静态成员变量可以通过类名来访问,也可以通过引用来访问(强烈不推荐),实例成员变量仅能通过引用访问。
37静态方法和实例方法
1、静态方法可以通过类名访问,也可以通过引用访问。实例方法仅能通过引用访问。
2、静态方法中仅能访问静态成员(静态方法和静态成员变量),而实例方法既可以访问静态成员,也可以访问实例成员。
3、静态方法中不能使用this与super,而实例方法中可以使用。
38静态成员变量的初始化
静态成员变量可以使用以下两种方式初始化:
1、声明处初始化
2、静态初始化块
声明处初始化与静态初始化块会按照类中声明的顺序执行。(谁先声明,谁先执行)
静态变量初始化,静态初始化块会在类初始化时执行,类初始化完毕后,才会调用main方法。(静态初始化早于main方法的调用)
39 final
1、final可以修饰成员变量,局部变量,方法参数、方法和类。
2、final修饰变量的时候,该变量仅能初始化(赋值)一次,以后便不能修改。通常我们也称为常量。Final修饰成员变量的时候,不能拿默认值当成初始值。(必须要显式进行初始化)。当final修饰基本数据类型时,变量本身的值不可更改。当final修饰引用类型时,引用本身的值不可更改,但引用指向的对象(对象的内部数据)是可以改变的。
3、final修饰方法的时候,该方法不能被子类重写(或隐藏)。
4、final修饰类时,该类为终极类,不可被子类所继承。
为什么要声明final变量?有什么好处?
1、声明final变量可以防止变量被意外的修改。
2、final变量可以避免魔幻数字的产生。
3、final变量可以提高程序的可维护性。
40类的封装
1、我们都在设计类的时候,不应该对去外界暴露更多的实现细节,而是应该将其隐藏。隐藏的方法就是尽可能将类的成员变量访问权限最小化,也就是使用private来修饰。
2、细节全部隐藏了,我们接下来需要提供类与外界交互的接口,这种接口是通过方法来实现的。我们将方法声明为public。我们习惯上使用getX与setX的方法。(getter与setter)
3、我们不直接访问类的成员变量,而是通过方法来简介的操作变量。