1.封装性的理解
- 为什么需要封装?封装的作用和含义?
我们要用洗衣机,只需要按一下开关和洗涤模式就可以了。没必要了解洗衣机内部的结构,没必要碰电动机… - 设计程序追求“高内聚,低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
低耦合:仅对外暴露少量的方法用于使用 - 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
1、 问题的引入:当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如setLegs)。同时,我们需要避免用户再次使用“对象.属性”的方式对属性进行赋值,则需要将属性声明为私有的(private),此时,针对于属性就体现了封装性。
2、 封装性的体现:
体现一:我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXXX)和设置(setXXX)此属性的值。
别的体现:①如上,②不对外暴露的私有方法,③单例模式,…
3、总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
通过一个例子体会封装性
public class AnimalTest{
public static void main(String[] args){
Animal a = new Animal();
a.name = "大黄";
a.age = 1;
a.setLegs = 4;
a.legs = 4;//报错:The field Animal.legs is not visible
}
}
class Animal{
String name;
private int age;//将age私有化
private int legs;//腿的个数
//对legs进行了封装(隐藏),使得外部不能直接调用legs,必须通过setLegs方法调用
//设置leg属性
public void setLegs(int l){
if(l >= 0 && l %2 == 0){
legs = l;
}else{
legs = 0;//或者提示异常,暂时未讲
}
}
//获取leg属性
public int getLegs(){
return legs;
}
//提供关于属性age的set和get方法
//设置age属性
public void setAge(int a){
age = a;
}
//获取age属性
public int getAge(){
return age;
}
public void eat(){
System.out.println("动物进食");
public void show(){
System.out.println("name = " + name + ",age = " + age + ",legs = " + legs);
}
}
}
2. 四种权限修饰符
封装性的体现,需要权限修饰符来配合。
Java权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
- Java规定的4种权限(从小到大排列):private、缺省(default)、protected、public
- 四种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
- 四种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类;对于类,只可以用public和缺省(default)修饰
- public类可以在一个项目中任意地方被访问,缺省类只可以被同一个包内部的类访问。
修饰符 | 内部类 | 同一个包 | 不同包的子类 | 同一个项目 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
public class PersonTest{
public static void main(String[] args){
Person p = new Person();
p.setAge(5);
p.getAge();
System.out.println("年龄为: " + p.getAge());
}
}
class Person{
private int age;
public void setAge(int i){
if(i >= 0 && i <= 130){
age = i;
}else{
throw new RuntimeException("传入的数据不合理!")//抛出异常
}
}
public int getAge(){
return age;
}
}
3. 类的成员之三:构造器
类的结构之三:构造器(或构造方法,constructor)
1、构造器的作用:
- 创建对象
- 初始化对象的属性
2、说明:
- 如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器(此时构造器的权限与该类的权限相同)
- 一旦显示定义了类的构造器之后,系统就不再提供默认的空参构造器
- 一个类中,至少会有一个构造器。
- 定义构造器的格式:权限修饰符 类名(形参列表){}
- 一个类中定义的多个构造器,彼此构成重载
3、构造器的特征
- 它具有与类相同的名称
- 它不声明返回值类型。(与声明为void不同)
- 不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
public class PersonTest{
public static void main(String[] args){
//创建Person类的对象:new + 构造器
Person p = new Person();//调用的是构造器
p1.talk();
Person p1 = new Person("Tom");//创建对象,并给对象进行初始化
System.out.println(p1.name);
class Person{
//属性
String name;
int age;
//构造器
public Person(){
System.out.println("Person()......");
}
public Person(String n){
name = n;
}
//方法(或函数)
public void talk(){
System.out.println("人讲话");
}
}
练习1:
package Constructor;
public class PersonTest{
public static void main(String[] args){
//创建Person类的对象:new + 构造器
Person p = new Person("Tom",21);//调用的是构造器
System.out.println("name = " + p.getName() + ",age = " + p.getAge());
}
}
class Person{
//属性
private String name;
private int age;
//构造器
public Person(){
age = 18;
}
public Person(String n,int a){
name = n;
age = a;
}
//方法(或函数)
//设置name属性
public void setName(String n){
name = n;
}
//获取name属性
public String getName(){
return name;
}
public void setAge(int i){
age = i;
}
public int getAge(){
return age;
}
}
练习2:
package Constructor;
public class TriangleTest {
public static void main(String[] args){
TriAngle t = new TriAngle(5,3);
TriAngle t1 = new TriAngle();
}
}
class TriAngle{
//属性
private double base;//底边
private double height;//高
//构造器
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height = h;
double area = (b * h)/2;
System.out.println("三角形的面积为:" + area);
}
//公共方法(或函数)
public void setBase(double b){
base = b;
}
public double getBase(){
return base;
}
public void setHeight(int h){
height = h;
}
public double getHeight(){
return height;
}
}
拓展知识1:JavaBean
JavaBean是一种Java语言写成的可重用组件。
所谓JavaBean,是指符合如下标准的java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set构造方法
拓展知识2:UML类图
知识3:属性赋值的先后顺序
①默认初始化——②显式初始化——③构造器中初始化——④通过“ 对象.属性” 或 “对象.方法” 的方式赋值
4. this关键字
this关键字的使用:
- this可以用来修饰属性、方法、构造器。
- this修饰属性和方法:
this理解为:当前对象 或 当前正在创建的对象(主要适用于构造器)
2.1 在类的方法中,我们可以使用 “this.属性” 或 “this.方法” 的方式,调用当前对象的属性或方法。但是,通常情况下,我们都选择省略 “this.” 。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用 “this.变量”的方式,表明此变量是属性,而非形参。
2.2 在类的构造器中,我们可以使用 “this.属性” 或 “this.方法” 的方式,调用当前正在创建的对象的属性或方法。但是,通常情况下,我们都选择省略 “this.” 。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用 “this.变量”的方式,表明此变量是属性,而非形参。 - this调用构造器
① 我们在类的构造中,可以显示的使用 “this(形参列表)” 的方式,调用本类中指定的其他构造器
② 构造器中不能通过 “this(形参列表)” 的方式调用自己(否则会死循环)
③ 如果一个类中有n个构造器,则最多有n - 1 个构造器中使用了 “this(形参列表)”(构造器a调用了构造器b后,构造器b不能再调用构造器a,否则也会死循环)。
④ 规定:“this(形参列表)” 必须声明在当前构造器的首行
⑤ 构造器内部:最多只能声明一个 “this(形参列表)” ,用来调用其他的构造器
package Constructor;
public class PersonTest{
public static void main(String[] args){
Person p1 = new Person();
p1.setAge(8);
p1.getAge();
System.out.println(p1.getAge());
}
}
class Person{
private String name;
private int age;
//构造器
public Person(){
}
public Person(String name){
this();//调用空参构造器
this.name = name ;
}
//方法
public void setName(String name){
this.name = name;//this可以理解为当前对象,this.name即为当前对象的属性
}
public String getName(){
return name;//或return this.name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;//或return this.age;
}
public void eat(){
System.out.println("人可以吃饭");
study();//或this.study();方法中调用方法
}
public void study(){
System.out.println("人可以学习");
}
}
练习:
public class BoyGirl {
public static void main(String[] args) {
Boy boy = new Boy();
Girl girl = new Girl();
girl.setName("lisa");
boy.marry(girl);
}
}
package Constructor;
public class Boy {
private String name;
private int age;
//构造器
public Boy(){
}
public Boy(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
public void marry(Girl girl){
System.out.println("我想娶" + girl.getName() );
}
public void shout(){
if(this.age >= 22){
System.out.println("你可以结婚了");
}else{
System.out.println("你不能结婚");
}
}
}
package Constructor;
public class Girl {
//属性
private String name;
private int age;
//构造器
public Girl(){
}
public Girl(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName(){
return this.name;
}
public void marry(Boy boy){
System.out.println("我想嫁给" + boy.getName() );
boy.marry(this);//this表示当前对象,谁调marry,boy就娶谁
//比较两个对象的大小
//return: 正数:当前对象大,负数:当前对象小,0:当前对象与形参对象相等
//当前对象指调用compare方法的对象,形参对象为传递的形参
public void compare(Girl girl){
if(this.age > girl.age){
//this.age为调compare方法的对象的属性,girl.age为为形参的属性
return 1;
}else if(this.age < girl.age){
return -1;
}else{
return 0;
}
}
}
5. package关键字
一、package关键字的使用
- 为了更好地实现项目中类的管理,提出包的概念
- 使用package声明类或接口所属的包,声明在源文件的首行
- 包,属于标识符,遵循标识符的命名规则和规范,“见名知意”(xxyyzz)
- 每 ”.“ 一次,就代表一层文件目录。
- 同一个包下,不能命名同名的接口、类;
不同的包下,可以命名同名的接口、类
体会:一个项目可能涉及到的类非常多,可以将类分到不同的包里,整体来看有很多包,当项目哪块出问题了,就去相应的包下去找,这样就使整个项目的结构变得很清晰。
6. import关键字
一、import关键字的使用
import:导入
- 在源文件中,显式使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构(类、接口),则并列写出即可
- 可以使用 “xxx.*” 的方式,表示可以导入xxx包下的所有结构
- 如果使用的类、接口是java.lang包下定义的,则可以省略import结构
- 如果使用的结构是本包下定义的,也可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全命名(包含包的路径在内的完整的路径名)的方式(例如:com.weiwei.exer1.Account acc1 = new com.weiwei.exer1.Account() )
- 使用 “xxx.* ”方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显式导入子包
- import static:导入指定类或接口中的静态结构(落脚点为属性、方法;不是类、接口)