Java SE 第九讲
面向对象之封装
1. 封装:类包含了数据与方法,将数据与方法放在一个类中就构成了封装。
2. 如何定义类?
修饰符 class 类的名字
{
//类的内容(包含了属性与方法)
}
3. 属性。属性需要定义在类中,又叫做成员变量;而定义在方法中的变量叫做局部变量。
如何定义属性?
public class Person
{
修饰符 类型属性名称;
}
如何使用属性?与方法一样,使用.运算符。首先需要生成类的实例,然后使用实例+”.”的方式来使用属性。
比如:
Person person = new Person();
person.age
4. 方法。如何定义方法?
修饰符 返回类型方法名称([参数1, 参数2, 参数3…])
{
//方法体
}
5. main方法是整个Java程序的入口点,如果类的定义中没有main方法,则程序无法执行。
6. 方法定义不能嵌套,也就说不能在一个方法中定义另外一个方法。方法只能定义在类中。
7. 关于方法的执行:首先需要定义方法,接下来就可以使用方法(调用方法),当方法调用完毕后,方法可以返回值。方法到底是否返回值是由方法的定义决定的。
8. 如何生成对象?通过类来生成对象(通常使用new关键字来生成对象)。
类名 变量名 = new 类名();
public class Person
{
}
Person person = new Person();
9. 方法调用可以通过对象调用,方法调用的形式是:
对象变量.方法名([参数值1, 参数值2, 参数值3….]);
10. 关于方法的注意事项:
1)在方法定义中,方法的返回类型与return后面的变量或常量类型保持一致。
2)在方法调用时,给方法传递的参数需要与方法定义时的参数保持一致(参数个数一致,参数类型一致)。
3)方法定义时的返回类型与接收方法返回值的变量类型保持一致。
11. public int add(int a, int b)
{
return a + b;
}
方法定义时的参数叫做形式参数。
int a = test.add(8, 3);
方法调用时所赋予的具体值叫做实际参数。
12. 关键字void表示方法不返回值。
13. 如果方法不返回值,那么声明方法的时候使用void关键字,在方法定义中可以有两种情况实现不返回值:
a) 不使用return语句。
eg:public void method(int a)
{
System.out.println(a);
}
b) 使用return,但return后面没有任何值或者变量,return后面只有一个分号,表示退出方法,返回到方法的调用端。
使用方式:
return;
eg;public void method(int a)
{
System.out.println(a);
return;
}
Java SE 第十讲
6. 局部变量使用前必须要声明并赋初值;成员变量使用前必须要声明,但可以不赋初值。
7. 成员变量与局部变量的联系与区别:
a) 无论是成员变量还是局部变量,使用前都需要声明(定义)。
b) 对于局部变量来说,使用前必须要初始化;对于成员变量来说,使用前可以不初始化。如果没有初始化成员变量就开始使用,那么每个类型的成员变量都有一个默认的初始值
i. byte、short、int、long类型的初始值为0
ii. float、double类型的初始值为0.0
iii. char类型的初始值‘\u0000’
iv. boolean类型的初始值为false
java 不会给自动局部变量赋初值。
重点:(程序分析)
public class People
{
intage = 20; -----------------------(7)
publicvoid change(People people)
{
//People people =new People();
//people= new People(); -----------------------(8)
people.age= 30; ------------------------(9)
}
public static void main(String args[])
{
Peoplepeople = new People(); -----------------------(1)
intage1 = people.age; -----------------------(2)
System.out.println(age1); -----------------------(3)
people.change(people); ------------------------(4)
intage2 = people.age; ------------------------(5)
System.out.println(age2); -----------------------(6)
}
}
程序执行分析(分析此程序可以借助画图):
一、语句people = new People();注释掉
程序从(1)开始执行,即main方法为程序入口:
(1)在内存里创建一个对象people,其属性age 的值为 20;
(2)在堆栈中创建一个变量(引用类型)age1(此变量里放着对象的地址),指向对象people(age = 20);
(3)输出
(4)引用方法change:(9)为应用方法的动作,即改变该对象的属性age的值为30;
(5)在堆栈中创建一个对象(引用类型)age2,指向对象people(age = 30);
(6)输出。
二、语句people = new People();生效
(1)同上;
(2)同上;
(3)同上;
(4)应用方法change:
(9)动作详解:
如果此语句生效,应用该方法时,会在内存里新生成一个对象,再把此对象的age值用20改为30。.
此步骤中的people指向新生成的对象。
(5)此步骤中的people指向第一个创建的对象。
(6)输出。
实例二:
public class ParamTest
{
publicvoid changePoint(Point point)
{
//Point point = newPoint();
point.x= 3;
point.y= 5;
}
publicstatic void main(String[] args)
{
ParamTestparamtest = new ParamTest();
Pointpoint = new Point();
paramtest.changePoint(point);
System.out.println(point.x);
System.out.println(point.y);
}
}
class Point
{
intx;
inty;
}
8. 引用类型(reference type):引用类型是用在对象上的。一个对象可以被多个引用所指向,但同一时刻,每个引用只能指向唯一的一个对象。如果一个对象被多个引用所指向,那么无论哪个引用对对象的属性进行了修改,都会反映到其他的引用当中。
eg;
People p0 = new People();
People p1 = new People();
p1 = p0;
p1、p2都指向了第一个对象,他们任何一个对对象的操作都会反映到其他应用中。p1同一时刻只能指向一个对象。
Java SE 第十一讲
1. 如果一个类包含了属性与方法,那么该类的每一个对象都具有自己的属性,但无论一个类有多少个对象,这些对象共享同一个方法。
详解:
每生成一个对象,就会在内存堆里的一个区域生成项目,包括对应的属性值。
但是类的方法是存在于内存的一个方法区的,每个对象调用的是同一个方法。
2. 关于方法参数传递的总结:对于Java中的方法参数传递,无论传递的是原生数据类型还是引用类型,统一是传值(pass by value)。
3. 什么类型的引用就能指向什么类型的对象,比如People类型的引用就能指向People类型的对象,但不能指向Student类型的对象。
比如:
People people = new People(); // 正确
People people = new Student(); //错误
4. 构造方法(Constructor):构造方法用于完成对象属性的初始化工作,构造方法的特点:
a) 构造方法的名字必须与类名完全一致(包含大小写)
b) 构造方法没有返回值,连void也不能出现。
c) 如果在定义一个类的时候,没有为类声明构造方法,那么Java编译器会自动为类添加一个没有参数且方法体为空的构造方法(默认的构造方法)
d) 如果在定义一个类的时候,为类声明了构造方法,那么Java编译器就不会再为类添加构造方法了。
e) 不能显式调用类的构造方法,构造方法通常是通过new关键字隐式调用。
5. new关键字在生成对象时完成了三件事情:
a) 为对象开辟内存空间。
寻到内存中未占用的空间,把对象放到内存里,表示对象已经生成。
b) 调用类的构造方法。
通过new关键字调用构造方法对类的对象的属性进行初始化。
c) 将生成的对象的地址返回。
eg;People people = new People();
首先开辟内存空间,存放People这个类的对象,接下来调用类的构造方法;把对象存放的地址的值返回给people这个变量,我们把这个存放对象地址值的变量叫做引用类型。people
是一个引用,而不是一个对象。真正的对象是new People()所生成的那个东西,对象在java中是看不到也摸不着的,我们只能通过引用来操纵对象。如果一个对象我们拿不到,但是要操作他,怎么办?就是通过这个对象的地址,即引用来间接的操作这个对象。这点跟C++是不一样的。把对象的地址传递给引用变量people,相当于people指向了这个对象。
引用类型变量值相同,表示指向相同的对象。
6. 默认的构造方法:构造方法没有参数且方法体为空。
public class ConstructorTest
{
publicConstructorTest()
{
//defaultconstructor
}
publicstatic void main(String[] args)
{
}
}
7. 使用new来生成对象的时候,后面的小括号()表示构造方法的参数列表,如果构造方法不接收参数,那么小括号中的内容为空;如果构造方法接收参数,那么小括号中的实际参数就需要与构造方法定义中的形式参数保持一致(参数数量一致、参数类型一致、按照顺序逐一赋值)。
对象放在堆里,引用可能在堆里,可能放在栈里,原生数据类型放在栈里。
在Java中进行方法的参数传递时,无论传递的是原生数据类型还是引用类型,参数传递方式统一是传值(pass by value)。Java中没有传引用(pass by reference)的概念。
下面我们会先来说说面向对象特征中的封装,但是在说它之前,还是先来说说信息的隐藏。这里,隐藏就像是封装的先锋官,没有了隐藏,封装就无从谈起。
我们已经知道了,Java中类和对象的概念,来自于我们的真实世界的抽象。那么我们在声明一个类,并根据类来创建对象的时候,就要估计它的真实合理性。
比如前面的学生类示例中,声明了age年龄属性。类型为整数类型。年龄是用来描述学生信息的。那么,如果运行其他的用户(比如其他的类或者对象)可以直接操作对象的属性,就会出现一些不必要的问题。
例如:将年龄(age)的值设置为1000。
虽然这在语法上是没有问题的,但是,我们知道,在现实世界中,这种情况是绝对不可能出现的。如果在程序的其它部分使用到了这个age,就可能会引起问题。
因此,一般而言,应该将属性定义为private(私有的)的,这样,只有类本身才可以访问这个属性,而外部程序或其他的类是不能访问它的(private的详细说明后面章节说明)。
通过定义不同的方法,并且将这些方法设置为public或Default来访问这些属性,这样,我们可以通过在方法中加入一些逻辑判断的方法来操作属性,例如,将年龄的上限设置为100,将下限设置为10等,这样就不会出现年龄为1000的情况了。一个改进后的Student类的定义如下(省略了其他属性和相应的方法):
public class Student1 {
// 定义属性
private int age;
// 定义属性“age”的设置方法
public void setAge(int s_age) {
if (s_age > 100) {
age = 100;
} else if (s_age < 10) {
age = 10;
} else {
age = s_age;
}
}
// 定义属性“age”的获取方法
public int getAge() {
return age;
}
}
这里先简单说明一下,在setAge方法中,if逻辑表示分支判断,很容易读出,当用户提供的参数年龄值大于100,则最多年龄可以记为100,如果小于10,则最小年龄可以记为10,如果年龄值在10到100的范围内,可以完成用户的赋值要求。
当然,这个逻辑可以根据实际情况来修改,这样一来,用户(其他的对象和类)就无法直接对年龄属性“胡作非为”了,他必须在我们的监督下完成操作。
这就是信息的隐藏。即将类中不想让其他用户直接操作的属性(或者方法,一般主要指属性)设置为private修饰,只允许在该类里面进行操作。
前面说了隐藏,那么顺理成章的,下面就是封装。
有了隐藏,封装的存在就有其重要的目的了。
前面我们已经将类中的属性修饰为private,隐藏起来,只能在类本身里面来操作。那么很多时候,属性的信息数据都是来自于其他的用户(其他类或者对象),那么其他的类和对象如何把信息数据交给对象,由相应的属性进行记录保存?
这就是封装了。
先看看封装的简单说明:
封装指的是将对象的状态信息(属性)和行为(方法)捆绑为一个逻辑单元的机制。
Java中通过将数据封装、声明为私有的(private),再提供一个或多个公开的(public)方法实现对该属性的操作。
从说明可以看出,封装是一个很实际的类的声明操作。我们将类的属性通过private声明为私有,但是同时也想让该属性在有限制的情况下提供给其他的类和对象来操作,就是封装,定义一个或者多个公开的public的方法实现对属性的操作。其他类和对象并不是直接对属性进行操作,而是将要操作的数据传递给公开的属性封装方法,由这些封装方法来完成具体的操作。
当然,具体如何实现操作,就是由类的声明人来定义了。就像是前面示例的set方法,当其他用户提供了年龄的数据,首先进行数据的检查,看是否符合逻辑要求,就像是输入数据的重复确认一样,防止错误的垃圾数据进入程序操作。
上面封装的说明,就是具体的封装操作。实际上,可以看做是Java类定义的一个基本要求。如无特殊要求,可以在声明类的时候,就按照封装的要求来进行类的声明。所有类的属性声明为private私有,在定义相关的属性的读写方法来实现对属性的操作。并且有这样的Java bean要求,一个Java bean就是一个最简单的java程序组件。java bean对类的属性的声明以及读写方法有这样的标准要求,这已经是Java开发的标准了,所有的java开发人员、Java软件、api等等,都遵循这个要求。如果你的代码复合Java bean的标准的话,会对你以后的额Java开发带来很大的便利。
Java bean要求类属性私有,属性的读方法:
public 属性的数据类型 get属性名称(){
}
其中,方法名中的属性名,第一个字母要大写。
属性的写方法:
public void set属性名称(属性数据类型 传入参数){
}
读写方法的内部逻辑可以根据实际情况来完成,方法的定义标准不能变,这样,Java平台会根据相应的get和set方法,识别出该方法分别是那个属性的读写方法(甚至即使在类里面,根本没有这个属性,话句话说,Java平台识别属性依据的是get和set方法)
Java bean先说到这里,等以后对Java了解跟多之后,这个标准就更容易理解了。
最后,补充说明一下封装的操作目的,这几个目的,在下一篇里面,结合我的实际开发经验,说说其重要的意义:
隐藏一个类的实现细节;
防止对封装数据的未经授权的访问。使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
有利于保证数据的完整性;
便于修改,增强代码的可维护性。
实现封装的关键是不要让方法直接访问其他类的属性,程序应该只能通过指定的方法 与对象的数据交互。封装赋予对象“黑盒”特性,这是实现重用性和可靠性的关键。