面向对象程序设计(Object Oriented Programming,OOP)是一种十分流行,并且功能强大的编程方法。其中最重要的两个概念是类和对象。
1.面向对象程序设计
面向对象程序设计,是将问题按照其功能分解为对象,并通过事件、消息驱动对象执行程序的设计方法。面向对象方法将数据和数据的处理封装在一起形成对象,并对数据进行抽象和隐蔽而形成类。面向对象程序设计以功能为中心,并且将数据和方法封装在一起,因此非常适合大型应用程序与系统程序的开发。
1.面向对象程序设计概述
- 传统的程序设计主要采用结构化的程序设计方法,也就是面向过程程序设计方法。根据软件需求,由上到下的进行逐步的细化,并将软件需求划分为一个个模块分别实现。可以说,这种方式是针对问题而求解的。随着用户需求的不断增加,软件规模越来越大,传统的面向过程的开发方式暴露出许多的缺点。
- 20世纪80年代,人们提出了面向对象程序设计 (Object Oriented Programming,OOP)的概念,在面向对象的世界中,程序员不需要考虑数据结构和功能函数,只需要关注对象就可以了。
- 对象就是客观世界中存在的人、事和物体等实体。在现实世界中,对象随处可见。如路边生长的花草树木、动物园里的飞禽走兽等。不过这里的花草树禽兽都是对同一事物的总称,这就是面向对象中的类(Class)。而对象就是符合某种类定义产生出的实例(Instance),虽然在日常生活中,习惯性用类称呼这些对象,但实际上看到的还是对象的实例,而不是一个雷。比如,看到树上落着一只麻雀,赋值麻雀属于麻雀类,即麻雀类的一个实例对象。由此可见。类只是一个抽象的称呼,而对象则是与现实生活中的事物相对应的实体。
- 类还有属性和方法,如孔雀的属性包括羽毛、翅膀等,方法包括开屏、飞翔等。因此,与现实世界事物的属性和行为相对应的就是对象的变量和方法。
2.面向对象程序设计的特点。
面向对象程序设计更贴近于人的具体的思考过程,程序具有更高的健壮性和可维护性。另外,它有利于系统模块的划分,能够高效的开发复杂的应用程序。面向对象的程序设计基本特性包括封装性、继承性和多态性。
1.封装性
- 封装性是面向对象程序设计最基本的特征。封装就是将现实世界的属性和行为组成一个整体,形成抽象的类。封装即意味着对外部隐藏内部的信息,外部用户直接调用对象提供的方法接口,无需理会其是如何具体实现。
- 例如,洗衣机就是一个具有封装行的对象,用户打开电源启动洗衣机,在洗衣机中添加衣服和水,最后按下启动按钮就可以了。但是启动按钮按键是一个复杂的过程,但是用户没必要直达这个过程是怎么实现的。
- 封装性既可以保护对象的内部数据,也可以避免外部操作导致的错误,并且降低查找问题的难度。封装性还可以提高程序的可维护性。当内部成员变量和实现方法改变时,他可以保持接口不变,因此,调用程序的接口就不需要改变。
2.继承性
- 继承性表示类之间父子层的关系。一个对象可以通过继承的方式获得另一个对象的属性。例如,华南虎是虎的一种,虎又是哺乳动物的一种,哺乳动物是动物的一种。
- 如果不使用类层次之间的概念,则每个类都需要明确定义各自的全部特征。通过类层次划分,子类在继承父类后,不仅可以自己添加新的属性和方法,还可以使用父类所有的属性和方法
3.多态性
- 多态性是指父类的属性和方法在被子类继承后具有不同的表现行为。多态性可以让子类中同一属性或同一方法与父类具有不同的语义。用户可以发送一个相同的信息,将对消息的处理细节分别安排到不同的对象中完成。因此,同一个消息可以有不同的运行结果。
- 例如,教师学生都是人的子类,都可以工作,但他们工作的性质是不同的。教师的工作是教学,而学生的工作是学习。
- 多态性和继承性有密切的关系,将通用的方法放置在层次较高的类中,然后利用继承性方法将方法重载放置在子类的层级中,可以完成相同信息不同响应的效果。在面向对象程序设计中利用方法重载实现多态性。
3.对象与类的关系
- 对象是可以理解面向对象(Object Oriented)技术的关键,我们周围存在的任何事物都可以看做是一个对象,如:狗、台灯、茶杯等,甚至我们自己都可以看作是一个对象。
- 现实世界里的对象都具有两个特性:属性(Attribute)和行为(Behavior)。例如,台灯具有的属性包括当前状态(开/关),具有的行为包括打开电源、关闭电源;手机具有的属性包括待机、通话、无信号等,具有的行为包括开机、关机、接通、挂断等。
- 面向对象程序设计中的对象在概念上类似现实生活里的对象:他们也是由属性和相关的行为组成的,对象把它的属性保存在成员变量(内部属性)中,并通过方法表现他的行为,方法操作对象的内部属性,并作为对象与对象通信的主要机制。隐藏内部属性并且要求所有信息交互都通过对象的方法来进行,这被称为内部封装(Encapsulation)
- 很多属于同一个类的对象具有相同的属性和方法。例如,所有的台灯都是为了照明,都必须具有开、关属性,也必须有打开、关闭这样的动作,每个台灯都具有相同的组件,所以在面向对象的术语中,台灯可以被称为台灯类(Class)的一个实例(Instance)。
- 类的举例
package example;
//这个类里面没有包含main()方法。因为这不是完整的应用程序,只是用于其他应用程序中台灯的 模板
class TableLamp {
boolean bswitch = false; //声明布尔变量,表示台灯的开关状态。
void Open() {
bswitch = true; //定义Open方法,设置bswitch的值为true
}
void Close() {
bswitch = false; //定义Close方法,设置bswitch的值为false
}
void PrintState() { //定义PrintState方法,判断bswitch状态
if(bswitch) {
System.out.println("台灯开了"); //当台灯处于开的状态时,输出信息
}else {
System.out.println("台灯关了");//当台灯处于关闭的状态时,输出信息
}
}
}
//创建一个CreateTableLamp类,创建两个独立的CreateTableLamp对象,并调用它们的方法。
class CreateTableLamps{
public static void main(String[] args) {
TableLamp lamp1 = new TableLamp(); //创建并初始化TableLamp对象Lamp1
lamp1.PrintState(); //调用PrintState()方法,显示当前状态
lamp1.Open(); //调用Open()方法,台灯打开
System.out.println("调用Open后");
lamp1.PrintState(); //调用PrintState()方法,显示当前状态
lamp1.Close();
System.out.println("调用Close后");
lamp1.PrintState(); //调用Close()方法,台灯关闭
}
}
2.类的定义
- 类是用来创建对象的模版,它包含被创建对象的状态描述和方法的定义。因此,要学习java编程,就必须学会怎样编写类。即怎样通过Java的语法描述一类事物公用的属性和行为。
- 属性通过域(field)表示,行为通过方法来实现。即方法操作属性形成一定的算法来实现一个具体的功能。类把数据和对数据的操作封装成一个整体。
- 实现一个简单的校园一卡通MasterCard类
package example;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
class MasterCard {
//类MasterCard的构造方法
public MasterCard(String i,String n,char c,double m,int year,int month,int day) {
//分别给各个域赋值
id = i; //初始化变量id
name = n; //初始化变量name;
sex = c; //初始化变量s;
money = m;//初始化变量m;
gc = new GregorianCalendar(year,month,day);//创建并初始化变量gc
releasedDay = gc.getTime();//初始化变量releasedDay
}
//获得卡的id
public String getId() {
return id;//返回变量id
}
//获得持卡人的姓名name
public String getName() {
return name;//返回变量name的引用
}
//获得持卡人的性别sex
public char getSex() {
return sex; //返回sex的值
}
public double getMoney() {//获得卡内余额money
return money;//返回money的值
}
public Date getReleasedDay() { //获得发卡时间信息releasedDay
return releasedDay;//返回变量releasedDay的引用
}
public String getStringofReleasedDay() { //获得发卡时间信息releasedDay的字符串信息
return new SimpleDateFormat("yyyy-MM-dd").format(releasedDay).toString();//返回格式化后的日期字符串
}
//往卡里存钱
public void charge(double m) {
money = money + m;
System.out.println("\n存入"+money);
}
//往卡里取钱
public void consume(double m) {
money = money - m;
System.out.println("\n消费"+money);
}
public void info() {
System.out.print("id = "+getId()+" name = "+getName()+" sex= "+ getSex()+" money= "+getMoney()+" releasedDay= "+getStringofReleasedDay());
}
private String id; //声明整形数据域,表示MaserCard的唯一标识
private String name; //声明字符串数据域,表示持卡人的姓名
private char sex; //声明字符数据域,表示持卡人的性别
private double money; //声明双精度数据域,表示卡内余额
private Date releasedDay; //声明Date数据域,表示发卡时间
private GregorianCalendar gc; //声明Gregorian数据域,表示日期
}
package example;
public class UseMasterCard {
//用new操作符调用构造方法
public static void main(String[] args) {
MasterCard mc = new MasterCard("1704423451","小明",'M',200,2019,4,23);//创建MasterCard对象mc
mc.info();
mc.consume(37.5);
mc.info();
mc.charge(56.9);
mc.info();
}
}
MasterCard类前面没有访问修饰符,而UseMasterCard类有一个public’访问修饰符
在一个源文件中,只能包含一个public修饰的类(公共类)
1.访问控制符
访问控制符指的是对程序其他部分进行访问和调用类、对象的域或方法时进行限制的修饰符。在三个层次上进行控制,包括类、域和方法。
域和方法的访问控制符包括public、private、protected、friendly
- public:被public修饰的类不仅可以被同一个包内其他的类访问,而且其他的包也可以访问该类。只是在访问和引用之前必须使用import语句导入该public类
MasterCard类包含1种构造方法和9种其他方法
//类MasterCard的构造方法
public MasterCard(String i,String n,char c,double m,int year,int month,int day)
//id的getter方法
public String getId()
//name的getter方法
public String getName()
//sex的getter方法
public char getSex()
//money的getter方法
public double getMoney()
//releasedDay的getter方法
public Date getReleasedDay()
//返回releasedDay的字符串信息
public String getStringofReleasedDay()
//充值方法
public void charge(double m)
//消费方法
public void consume(double m)
//输出卡信息的方法
public void info()
这个类中所有的方法都被标记为public。
private String id; //声明整形数据域,表示MaserCard的唯一标识
private String name; //声明字符串数据域,表示持卡人的姓名
private char sex; //声明字符数据域,表示持卡人的性别
private double money; //声明双精度数据域,表示卡内余额
private Date releasedDay; //声明Date数据域,表示发卡时间
private GregorianCalendar gc; //声明Gregorian数据域,表示日期
这个类中包括5个域,域前面的关键字都是private
2.构造方法
构造方法也被称为构造器,用于为对象中所有成员变量(域)进行初始化。在创建对象时立刻被调用。
构造方法是一种特殊的方法,他的名字必须与所在类的名字完全相同,并且没有返回值;也不需要关键字void进行标识。
程序在定义MasterCard类的时候,定义了一个MasterCard类的构造方法
public MasterCard(String i,String n,char c,double m,int year,int month,int day) {
//分别给各个域赋值
id = i; //初始化变量id
name = n; //初始化变量name;
sex = c; //初始化变量s;
money = m;//初始化变量m;
gc = new GregorianCalendar(year,month,day);//创建并初始化变量gc
releasedDay = gc.getTime();//初始化变量releasedDay
}
- 构造方法与类同名
- 每个类可以有一个以上的构造方法
- 构造方法可以有0个或0个以上的参数
- 构造方法没有返回值
- 构造方法必须伴随new操作符使用
3.方法的参数
java中方法的参数有两种类型,一种是值传递,一种是引用传递。
程序MasterCard中方法consume()和save()就是通过值传递的方法
//往卡里存钱
public void charge(double m) {
money = money + m;
System.out.println("\n存入"+money);
}
//往卡里取钱
public void consume(double m) {
money = money - m;
System.out.println("\n消费"+money);
}
这两个方法都有两个参数,consume()方法的第一个参数double m 叫显式参数,在方法名后面的括号中。隐式参数是调用该方法的对象,常用关键字this来表示。
方法的参数只在该方法的内部生效。
操作的是参数money,而类的money域被屏蔽了。java为了解决这类问题,使用关键字this代表隐式参数,又来区分域和局部变量。
4.封装与隐藏
封装就是将现实的事物抽象,并将抽象化的数据和行为组合在一起, 也就是将数据和数据操作的方法放在一起,形成类的概念,其中数据和方法称为类的成员。
封装的目的是提高程序的安全性和增强代码的重用性。隐藏是指将类的描述部分(域)和具体实现细节(方法)分开,这就使得程序员在使用这个类的时候无需知道类的具体实现细节,而只通过调用接口就可以使用该类的成员。
程序MasterCard里的访问方法
//id的getter方法
public String getId()
//name的getter方法
public String getName()
//sex的getter方法
public char getSex()
//money的getter方法
public double getMoney()
//releasedDay的getter方法
public Date getReleasedDay()
//返回releasedDay的字符串信息
public String getStringofReleasedDay()
访问方法一般是以字母get()开头命名的方法。变更方法允许改变对象的数据,一般以set开头
public void serId(String i){
id = i; //设置id的值
}
public void serName(String n){
name = n; //设置name的值
}
public void setSex(char s){
sex = s; //设置sex的值
}
public void setMoney(double m){
money = m; //设置name的值
}
public void setReleasedDay(Date d){
releasedDay = d; //初始化releasedDay
}
如果试图把一个不合法的值赋值给域,就会出现错误。想要更好的设计一个类,更好的实现类的封装和隐藏,可以使用public和private标识符进行限定。
- 封装和隐藏可以将一个软件划分为多个模块,每个模块可以独立开发、测试、修改等。
- 封装和隐藏可以让各个模块独立、平行的开发,加快软件开发进度。
- 发生错误时,排查范围明确
- 修改一个模块后,不会影响其他的模块。
- 封装和隐藏可以增强代码的复用性。
5.finalize()方法
- java中使用finalize()方法代替C++中的析构方法。设置析构方法的目的是释放资源,同时销毁自身,
- 析构方法的一个关键技术是垃圾收集器。当java要创建一个对象时,java虚拟机会自动为对象分配一个空间。当停止使用对象时,java虚拟机将通过垃圾收集器将对象标记为释放状态。
- 用户自己实现的java类可以覆盖finalize()方法,并且在子类中也可以调用父类的finalize方法。但是finalize方法不可以自动实现调用,必须通过super.finalize()语句实现,
- 垃圾收集器调用finalize()方法的时间是不确定的,用户也无法控制调用的时间。有时需要通过其他手段来释放程序中占用的资源,如可以自动声明一个destroy()方法。最好将destroy()方法放到finalize()方法中,这样会更安全
3.静态域与静态方法
main()方法都使用关键字static标识。关键字static可以用来标识变量和方法,分别称为静态域和静态方法。
1.静态域
如果将域定义为static,则那么该域是属于整个类的变量,而不属于某个实例。
所有对象共用这个类的静态域,因为一个静态域可以在不同对象内使用,可以用于不同对象之间的通信。
声明静态域和其他域声明相同,只要在类型之前加上关键字static。
private static int flag = 0;
//一般定义静态域的时候,要给他初始化一个值
package example;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
class MasterCard2 {
//类MasterCard的构造方法
public MasterCard2(String i,String n,char c,double m,int year,int month,int day) {
//分别给各个域赋值
id = i; //初始化变量id
name = n; //初始化变量name;
sex = c; //初始化变量s;
money = m;//初始化变量m;
gc = new GregorianCalendar(year,month,day);//创建并初始化变量gc
releasedDay = gc.getTime();//初始化变量releasedDay
}
//设置卡的类型
public void setFlag(int f) {
flag = f;//设置flag的值
}
public int getFlag(){//获得卡的类型
return flag;
}
private static int flag;//声明整形数据域,表示不同类型的卡
private String id; //声明整形数据域,表示MaserCard的唯一标识
private String name; //声明字符串数据域,表示持卡人的姓名
private char sex; //声明字符数据域,表示持卡人的性别
private double money; //声明双精度数据域,表示卡内余额
private Date releasedDay; //声明Date数据域,表示发卡时间
private GregorianCalendar gc; //声明Gregorian数据域,表示日期
}
package example;
public class UseMasterCard2 {
//用new操作符调用构造方法
public static void main(String[] args) {
MasterCard2[] mc = new MasterCard2[2];
mc[0] = new MasterCard2("193452","小明",'M',200,2022,3,12);
mc[1] = new MasterCard2("233221","小白",'F',210,2022,3,12);
//输出卡的默认类型
System.out.println("mc[0].getFlag()="+mc[0].getFlag());
mc[0].setFlag(5);//设置flag的类型为5
System.out.println("mc[0].getFlag(5)");
System.out.println("mc[1].getFlag()="+mc[1].getFlag());
}
}
2.静态方法
静态方法是不能对对象访问操作的方法。这种方法没有this参数。静态方法不能操作对象,所以在静态方法中不能访问实例域,但可以访问本身的静态域。
- MasterCard.java程序完成了绝大部分功能,但是不能单独运行,因为没有main方法。
- 每个类都可以添加一个main方法用测试。这是对类进行单元测试的一个常用技巧
3.Math类
java.lang,Math提供了一系列静态方法用于科学计算。
Math是一个工具类,在解决与数学有关的一些问题时起到非常重要的作用
package example;
public class MathDemo {
public static void main(String[] args) {
// 通过Math.random()方法随机获取double类型的数据
double a = Math.random();
double b = Math.random();
System.out.println("Math.random()A=" + a);
System.out.println("Math.random()B=" + b);
System.out.println("Math.sqrt(45) = " +Math.sqrt(45)); //对45开平方
System.out.println("Math.round(b*10)="+ Math.round(b*10)); //返回接近的整数
System.out.println("Math.log(Math.log(Math.pow(Math,E,15))="+Math.log(Math.pow(Math.E, 15)));//求e的15次幂,取该数的自然对数
System.out.println("Math.toRadians(75)"+Math.toRadians(75)); //角度转换为相等的弧度
System.out.println("Math.toDegrees(Math.Pi/6)"+Math.toDegrees(Math.PI/2));//弧度转换为相等的角度
}
}
4.构造方法
1.默认域初始化
如果构造方法中没有显式给域赋初值,他就会自动的赋默认值,
例如,在MasterCard中,如果构造方法中没有对域进行初始化,则默认值如下:
id = null;
name = null;
sex = '\u000';
money = 0;
releasedDay = null;
2.默认域构造方法
如果在编写一个类的时候没有编写构造方法,那么系统会自动提供一个默认构造方法,这个份默认构造方法会将所有的实例域设为默认值。
//显式声明默认构造方法
public MasterCard{
id = null;
name = null;
sex = '\u000';
money = 0;
releasedDay = null;
}
如果在类中提供了一个或多个构造方法,那么系统就不会再提供默认的构造方法。
在编写类时,即使给出了一个很简单的构造方法,想要实现new ClassName()就必须显式的定义一个不带参的构造方法
(默认构造方法)。如果希望所有的域都被赋初值,则只需要构造方法为空(无实现语句)即可。
3.拷贝构造方法
拷贝构造方法就是用类的一个对象来初始化这个类的另外一个对象,一个拷贝构造方法是只有一个与类具有相同类型参数的构造方法。
//MasterCard类的拷贝构造方法
public MasterCard(MasterCard mastercard){
if(mastercard == null){
System.out.println("对象为空,发生错误退出");
System.exit(0);
}
//设置其他域信息
setMoreInfo(mastercard.id,mastercard.name,mastercard.sex,mastercard.money);
releasedDay = (Date)mastercard.releasedDay.clone();//完成releasedDay数据拷贝
}
4.构造方法的重载
重载(Overloading)是OOP的概念,表示方法名字相同,但是参数列表不同的情形,这些同名不同参数的方法互相称为重载方法。调用方法时通过传递给他们不同参数个数和参数类型来决定使用哪个具体方法,这就称为多态性。
*例如,运算符"-',当前后两个操作数时,编译器把他当做减号。而当右边只有一个操作数时,编译器把它当做一元操作符号。
如果,多个方法有相同的名字、不同的参数就会发生重载。编译器通过各个方法给出的参数类型与调用时的实参进行匹配。
public MasterCard(String i,String n,char s,double m){
id = i; //给数据id赋值
name = n; //给数据域name赋值
sex = s; //给数据域sex赋值
money = m; //给数据域name赋值
releasedDay = new Date(); //初始化releasedDay
}
5.构造方法的调用
除了使用关键字this作为成员方法的隐式参数,this还有另外一种含义。如果构造方法的第一个语句形如this(…),那么这个构造方法调用这个类的另外一个构造方法。
public MasterCard(String i,String n,char s,double m,double initial){
this(i,n,s,m);//调用该类的MasterCard(String i,String n,char s,double m)构造方法
money += initial
}
6.创建初始化块
创建初始化块是java中初始化数据域的方法。在声明一个类的时候,可以包含多个程序块。例如,在域之后添加下面的代码块:
{
id = "s000000000";
name = " ";
sex = 'F';
money = 0;
releasedDay = new Date();
}
在这个例子中,无论使用哪个构造器构造对象,id域都在对象初始化中被初始化。
如果在初始化前面用关键词static标识,则该块被称为静态初始化块。
5.包
java通过包的概念组织类,包类似计算机中的文件夹。使用java包的概念可以避免名字冲突,并且可以在访问商做一定的限制。
1.包的概念
包(Package)是用来组织一组相关类和接口的名称空间,可以认为包类似于计算机中不同的文件夹。由于java面向对象程序设计由成百上千个类组成。将类和接口放在不同的包下,则类和接口具有很强的组织性。
包最重要的作用 就是保证类名的唯一性。如java包与java.lang把毫无关系,每一个都是独立的类集合。
2.包的导入
一个类可以使用所属包中所有类及其他包中的公共类。第一类方式就是在类名前添加包含包名的完整名字。第二类方式就是使用关键字import导入包。
import语句应该位于源文件的顶部,但是必须在package语句的后面。
如果程序需要导入包内的多个类时,那么可以通过“*”导入整个包的类。
如果对一个类的引用不明确,即编译器无法确定Date是java.sql包中的类java.sql.Date,还是java.util包中的类java.util.Date。这时候,可以增加一个import导入语句来限定这个类。
import java.util.*;
import java.sql.*;
import java.util.Date;
import与C++中的#include之间没有共同之处
3.包名域目录的关系
包名不是任意的修饰符,而是包中类似的目录名的一种形式。
在window系统中,路径目录是用反斜杠“\”隔开的。
程序所在目录的所有类属于一个未命名的包,这个包称为默认包。所在目录被配置到classpath中时,默认包中的所有类均无需导入。
package example; //声明包
public class MyPoint {
//获得x的值
public int getX() {
return x; //返回成员变量x的值
}
//获得y的值
public int getY() {
return y; //返回成员变量y的值
}
//设置x的值
public void sexX(int x) {
this.x = x; //设置成员变量x的值
}
//设置y的值
public void setY(int y) {
this.y = y; //设置成员变量y的值
}
private int x; //定义整形数据x,表示坐标的x轴值
private int y; //定义整形数据y,表示坐标的y轴值
}
4.静态导入
import语句不仅可以导入类,还可以导入静态方法和静态域
package example;
import static java.lang.System.*;
public class HelloWorld {
public static void main(String[] args) {
out.println("Hello World!");
}
}
*程序通过import static java.lang.System.语句完成了静态导入。则接下来就可以使用out.println代替System.out.println完成字符串信息输出
5.java包介绍
- java.* 包:这个包 是JDK的核心部分
- javax.*包:这个包时对java.*包的拓展和延伸
6.类设计技巧
- 一定要将数据域设置为private
只有将数据域设置为private类型,才能保证类的封装性 - 一定要初始化数据
虽然java利用初始化对象的域,但是绝对不能依赖于这些默认值,必须显示的初始化域。 - 不要再类中使用过多的基本类型
在程序中应使用应用类代替多个相关基本类型变量,这样就容易被理解和修改。 - 并非所有字段度需要独自的字段访问方法和更改方法
5通常情况下,如果需要获取和设置员工的新进,那类必须提供想要的访问方法和更改方法。但对于校园一卡通的发卡日期,在对象构造之后就不会改变时间。 - 为类定义使用标准格式
通常按照下面的顺序列出类的内容
共有特性
包作用域特性
私有属性
具体到每一部分,顺序如下
实例方法
静态方法
实例方法
表态字段
因为java是面向对象程序设计语言,因此使用者对公开接口、方法比对私有接口、内部数据更感兴趣。所以将方法放在域的前面,将共有方法放在私有方法前面。
6. 分解职责太多的类
当一个类很明显可以由其他几个类组成,并且这几个类都不是过于简单时,可以拆解。
7. 让类和方法的名字反应他们的职责
变量名应该反应他们的意义,同样,类和方法也需要表示他们各自的意义和职责。而对于方法,访问方法以小写get开始(getSalary),更改方法以小写set开始(setSalary)
7.实例:完善MasterCard类
1.构造方法
package example;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
class MasterCard3 {
//类MasterCard的构造方法1
public MasterCard3(String i,String n,char c,double m,int year,int month,int day) {
//判断name是否为空。若为空,则发生错误,程序终止
if(name==null) {
System.out.println("创建对象的参数不能为空");
System.exit(0); //退出程序
}
//判断输入的年月日是否符合规范
if(year<1900||month<1||month>12||Day<0||day>31) {
System.out.println("信息输入有误");//输出字符串信息
System.exit(0);//退出程序
}
//对其他域进行初始化
this.name = name;
this.sex = sex;
this.money = money;
gc = new GregorianCalendar(year,month,day);
releasedDay = gc.getTime();
}
//类MasterCard的构造方法2
public MasterCard3(String name,char sex,double money) {
//判断name是否为空。若为空,则发生错误,程序终止
if(name==null) {
System.out.println("创建对象的参数不能为空");
System.exit(0); //退出程序
}
this.name = name;
this.sex = sex;
this.money = money;
releasedDay = new Date();
}
//类MasterCard的构造方法3
public MasterCard3(String name,char sex,double money,double initial) {
this(name,sex,money);
this.money += initial;
}
//类MasterCard的构造方法4
public MasterCard3(MasterCard3 mastercard) {
//判断name是否为空。若为空,则发生错误,程序终止
if(name==null) {
System.out.println("创建对象的参数不能为空");
System.exit(0); //退出程序
}
releasedDay = (Date)mastercard.releasedDay.clone();
}
//MasterCard3无参构造方法
public MasterCard3() {
name = "noname";
sex = '\u0000';
money = 0;
releasedDay = new Date();
}
private String id; //声明整形数据域,表示MaserCard的唯一标识
private String name; //声明字符串数据域,表示持卡人的姓名
private char sex; //声明字符数据域,表示持卡人的性别
private double money; //声明双精度数据域,表示卡内余额
private Date releasedDay; //声明Date数据域,表示发卡时间
private GregorianCalendar gc; //声明Gregorian数据域,表示日期
}
2.setter()方法
getter()和setter()方法通常被称为存取器方法,通常使用setter()方法更改私有阈。
MasterCard3提供了3个setter()方法,分别用于设置域name、sex、money的值。
//设置name的值
public void setName(String name) {
this.name = name;//,给成员变量name赋值
}
//设置sex的值
public void setSex(char sex) {
if(sex!='F'||sex!='M'||sex!='f'||sex!='m') {
System.out.println("信息输入有误");//输出字符串信息
System.exit(0);//退出程序
}
this.sex = sex;//给成员变量sex赋值
}
//设置money的值
public void setMoney(double money) {
this.money = money; //给成员变量money赋值
}
3.getter()方法
getter()方法用于访问私有域的值
//取得name的值
pub
lic String getName() {
return name; //返回成员变量name
}
//取得sex的值
public char getSex() {
return sex; //返回成员变量sex
}
//取得money的值
public double getMoney(){
return money; //返回成员变量money
}
//取得releasedDay的一份拷贝
public Date getReleasedDay() {
return (Date)releasedDay.clone();
}
4.其他方法
除了构造方法、存取器方法、MasterCard还包括一些业务性的方法。如卡的充值、消费、信息输入等。
public String getStringofReleasedDay() {
return new SimpleDateFormat("yyyy-MM-dd").format(releasedDay).toString();
}
public void consume(double money) {
this.money= this.money - money;
}
public void charge(double m) {
if(m<0) {
System.out.println("充值失误,不能为负");
System.exit(0);
}
this.money = this.money + m;
}
public void serMoreInfo(String id,String name,char sex,double money) {
this.id = id;
this.name = name;
this.sex = sex;
this.money = money;
}
public void printfor() {
System.out.println("name = "+name + "sex = "+sex +" money="+money + "releaseDay="+releasedDay.toString()) ;
}
8.main()方法
- java虚拟机将从指定类中的main()方法开始运行。因此,为了使程序代码能够运行,在类的源代码中必须包含一个main()方法
- main()方法必须被声明为public,用花括号划分程序的各个部分(通常被称为块),java中任何方法的代码必须以{ 开始,以}结束
package example;
public class UseMasterCard3 {
//用new操作符调用构造方法
public static void main(String[] args) {
MasterCard3[] mc = new MasterCard3[2];
// mc[0] = new MasterCard3("小明", 'M',200,2022,3,12);
mc[1] = new MasterCard3("小白",'F',27,210);
//输出卡的默认类型
System.out.println("mc[0].getName()="+mc[0].getName());
System.out.println("mc[0].getName()="+mc[0].getMoney());
System.out.println("mc[1].getName()="+mc[1].getName());
System.out.println("充值前"+mc[1].getMoney());
mc[1].charge(50);
System.out.println("充值后"+mc[1].getMoney());
}
}
9.拓展训练
1.摄氏温度与华氏温度的转换
package example;
import java.util.Scanner;
public class TemperatureConverter {
public double getFahrenheit(double celsius) {
double fahrenheit = 1.8 * celsius + 32 ;//计算华氏温度
return fahrenheit; //返回华氏温度
}
public static void main(String[] args) {
System.out.println("请输入要转换的温度(单位:摄氏度)");
Scanner in = new Scanner(System.in);
double celsius = in.nextDouble();
TemperatureConverter converter = new TemperatureConverter();//创建类的对象
double fahrenheit = converter.getFahrenheit(celsius);//转换温度为华氏温度
System.out.println("转换完成的温度为 "+ fahrenheit);
in.close();//输出in释放的资源
}
}
使用对象方法时要先创建一个方法所在类的对象,然后通过对象来调用这个方法
2.汉诺塔问题的解决
package example;
public class HanoiTower {
public static void main(String[] args) {
int nDisks = 3;
moveDish(nDisks,'A','B','C');
}
public static void moveDish(int level, char from, char inter, char to) {
if(level==1) {
System.out.println("1号盘子:"+from+"--->"+to);
}else {
moveDish(level-1,from,inter,to);
System.out.println(level + "号盘子:"+from+"--->"+inter);
moveDish(level-1,to,inter,from);
System.out.println(level + "号盘子:"+inter+"--->"+to);
moveDish(level-1,from,inter,to);
}
}
}
-
为了将N个盘子从A移到C,需要将第N个盘子上面的N-1个盘子移动到B上,同理,需要将第N-1个盘子从B移动到C上,需要将N-2个盘子移动到A上
-
递归问题:对于一个复杂的问题,把问题分解为若干个相对简单的子问题。直到子问题能求解
-
递归问题的关键:1.找到递归出口 2.逐步向出口逼近
10.技术疑惑
- final、finally、finalize的区别
- final是java的关键字,是一个修饰符
- finally是java的一种异常处理机制
- finalize是java中的一个方法名
2.静态代码块和静态方法的区别
当类被载入时,静态代码块立刻被执行。因此,静态代码块常用来执行类属性的初始化工作
静态方法在类加载的时候就被加载,但只有被调用的时候才被执行。