一、封装性
在研究封装之前,首先先观察如下一段代码:
class Book {// 定义一个新的类
String title;// 书的名字
double price;// 书的价格
public void getInfo() {// 此方法将由对象调用
System.out.println("图书名称:" + title + "价格:" + price);
}
}
public class TestDemo {
public static void main(String args[]) {
Book book = new Book();
book.title = "Java开发";
book.price = -89.9;
book.getInfo();
}
}
以上的代码之中没有语法错误,但是却存在业务逻辑上的错误,因为没有一本书的价格是负的。而造成此类问题的核心的关键在于:对象可以在一个类的外部直接访问属性。
那么现在首先要解决的问题是需要将Book类中的属性设置为对外不可见,只能够针对于本类访问。用private关键字来定义属性。
范例:修改之前的程序
class Book {// 定义一个新的类 private String title;// 书的名字 private double price;// 书的价格 public void getInfo() {// 此方法将由对象调用 System.out.println("图书名称:" + title + "价格:" + price); } } public class TestDemo { public static void main(String args[]) { Book book = new Book(); book.title = "Java开发";// 字段 Book.title 不可视 book.price = -89.9;// 字段 Book.price 不可视 book.getInfo(); } }
在访问属性的时候发现,外部的对象无法再直接调用类中的属性了,所以现在等于是属性对外部不可见。
但是如果要想使程序正常使用,那么必须想办法让外部的程序可以操作类的属性,所以在开发之中,针对于属性有这样一种定义:所有在类中定义的属性都要求使用private声明,如果属性需要被外部所使用,那么按照要求定义相应的setter、getter方法,以String title为例。
- setter方法:主要是设置内容,public void setTitle(String t);有参数
- getter方法:主要是取得属性内容,public String getTitle();无参数
范例:为Book类中的封装属性设置setter、getter方法
class Book {// 定义一个新的类 private String title;// 书的名字 private double price;// 书的价格 public void setTitle(String t) { title = t; } public void setPrice(double p) { price = p; } public String getTitle() { return title; } public double getPrice() { return price; } public void getInfo() {// 此方法将由对象调用 System.out.println("图书名称:" + title + "价格:" + price); } } public class TestDemo { public static void main(String args[]) { Book book = new Book(); book.setTitle("Java开发"); book.setPrice(-89.9); book.getInfo(); } }
如果真的需要加入检查错误的代码,那么应该在setter之中增加,因为getter只是简单地返回数据。
范例:增加验证
public void setPrice(double p) { if (p > 0.0) { price = p; } }
对于数据的验证部分,在标准开发之中应该是由其它的辅助代码完成的,而在实际开发之中,setter往往是简单的设置数据,getter是简单的取得数据。
二、构造方法与匿名对象
现在我们已经清楚了对象的产生格式:
①类名称 ②对象名称 = ③new ④类名称()
- ①类名称:规定了对象的类型,即:对象可以使用哪些属性与哪些方法,都是由类定义的;
- ②对象名称:如果要想使用对象,需要有一个名字,这是一个唯一的标记;
- ③new:开辟新的堆内存空间,如果没有此语句,对象无法实例化;
- ④类名称():调用了一个和类名称一样的方法,这就是构造方法。
通过以上分析,可以发现,所有的构造方法一直在被我们调用。但是,我们从来没有去定义一个构造方法,之所以能够使用,是因为在整个Java类之中,为了保证程序可以正常执行,那么即使用户没有定义任何的构造方法,也会在程序编译之后自动的为类里面增加一个没有参数,方法名称与类名称相同,没有返回值的构造方法。
构造方法的定义原则:方法名称与类名称相同,没有返回值声明。
class Book {
public Book() {// 无参的,无返回值的构造方法
}
}
如果在Book类里面没有写上以上的构造方法,那么也会自动生成一个。
构造方法的作用是什么呢?
范例:观察代码
public class Demo { public static void main(String[] args) { Book book = null; book = new Book(); } } class Book { public Book() { System.out.println("********"); } }
可以发现所有的构造方法所有的对象都在对象使用关键字new实例化的时候被默认调用。
构造方法与普通方法的最大区别:
- 构造方法是在实例化新(new)对象的时候只调用一次。
- 普通方法是在实例化对象产生之后可以随意调用多次。
构造方法在对象实例化时使用有什么作用呢?
class Book {// 定义一个新的类
private String title;// 书的名字
private double price;// 书的价格
public void setTitle(String t) {
title = t;
}
public void setPrice(double p) {
if (p > 0.0) {
price = p;
}
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
public void getInfo() {// 此方法将由对象调用
System.out.println("图书名称:" + title + "价格:" + price);
}
}
public class TestDemo {
public static void main(String args[]) {
Book book = new Book();//使用默认生成的构造方法
book.setTitle("Java开发");
book.setPrice(-89.9);
book.getInfo();
}
}
本程序是先实例化了Book对象,而后利用Book类的实例化对象去调用类中定义的setter方法。如果要想设置全部属性的内容,那么一定要调用多次构造方法。
范例:定义构造方法
class Book {// 定义一个新的类 private String title;// 书的名字 private double price;// 书的价格 // 已经明确定义了一个构造,默认的构造将不再自动生成 public Book(String t, double p) { title = t; setPrice(p);// 调用本类中定义的setter方法 } public void setTitle(String t) { title = t; } public void setPrice(double p) { if (p > 0.0) { price = p; } } public String getTitle() { return title; } public double getPrice() { return price; } public void getInfo() {// 此方法将由对象调用 System.out.println("图书名称:" + title + "价格:" + price); } } public class TestDemo { public static void main(String args[]) { //在实例化对象的同时将所需要的类属性传递到对象的构造方法里 Book book = new Book("Java开发",89.9);// 使用默认生成的构造方法 book.getInfo(); } }
在实际的工作之中,构造方法的核心作用:在类对象实例化的时候设置属性的初始化内容。构造方法是为属性初始化准备的。
如果一个类之中明确的定义了一个构造方法,那么不会再自动生成默认的构造方法,即:一个类之中至少保留有一个构造方法。
另外,既然构造方法也属于方法行列,那么可以针对于构造方法进行重载,但是构造方法由于其定义的特殊性,所以在构造方法重载时,要求只注意参数的类型及个数即可。
范例:构造方法重载
class Book { public Book() { System.out.println("无参构造"); } public Book(String t) { System.out.println("有一个参数的构造"); } public Book(String t, double p) { System.out.println("有两个参数的构造"); } } public class Demo { public static void main(String[] args) { Book ba = new Book("Java"); } }
在定义构造方法重载的时候有一点代码编写要求:请按照参数的个数进行升序或降序排列。
遗留问题:在定义一个类的时候可以为属性直接设置默认值,但是这个默认值只有在构造执行完后才会设置,否则不会设置,而构造方法是属于整个对象构造过程的最后一步,即:是留给用户处理的步骤。
在对象实例化的过程之中,一定会经历类的加载,内存的分配,默认值的设置,构造方法。
class Book {
private String title = "Java开发";
public Book() {//title现在的默认值跟此构造方法没关系
}
}
本程序之中,只有在整个构造都完成之后,才会真正将我们的“Java开发”这个字符串的内容设置给title属性,在没有构造之前title都是其对应数据类型的默认值。
依照构造方法的概念使用匿名对象。之前定义的都属于有名对象,所有的对象都给了一个名字,但是这个名字真正使用的时候调用的肯定是堆内存空间,即:真实的对象信息都保存在了堆内存空间之中,那么如果没有栈指向的对象就称为匿名对象。
class Book {// 定义一个新的类
private String title;// 书的名字
private double price;// 书的价格
// 已经明确定义了一个构造,默认的构造将不再自动生成
public Book(String t, double p) {
title = t;
setPrice(p);// 调用本类中定义的setter方法
}
public void setTitle(String t) {
title = t;
}
public void setPrice(double p) {
if (p > 0.0) {
price = p;
}
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
public void getInfo() {// 此方法将由对象调用
System.out.println("图书名称:" + title + "价格:" + price);
}
}
public class TestDemo {
public static void main(String args[]) {
new Book("Java开发",89.9).getInfo();
}
}
但是匿名对象由于没有其它对象对其进行引用,所以只能使用一次,一次之后该对象空间就将成为垃圾等待回收。
三、综合实战:简单Java类
现在要求开发一个雇员的类,里面包含有雇员编号、姓名、职位、基本工资、佣金。
这种功能的类在开发之中成为简单Java类,因为这些类里面不会包含有过于复杂的程序逻辑。
对于简单Java类而言,那么现在可以给出它的第一种开发形式:
- 类名称必须存在有意义,例如:Book、Emp;
- 类之中所有的属性必须用private封装,封装后的属性必须提供有setter、getter;
- 类之中可以提供有任意多个构造方法,但是必须保留有一个无参构造方法;
- 类之中不允许出现任何的输出语句,所有的信息输出必须交给被调用处输出。
- 类之中需要提供有一个取得对象完整信息的方法,暂定为:getInfo(),而且返回String型数据。
范例:开发程序类
class Emp { private int empno; private String ename; private String job; private double sal; private double comm; public Emp() { } public Emp(int eno, String ena, String j, double s, double c) { empno = eno; ename = ena; job = j; sal = s; comm = c; } public void setEmpno(int e) { empno = e; } public void setEname(String e) { ename = e; } public void setJob(String j) { job = j; } public void setSal(double s) { sal = s; } public void setComm(double c) { comm = c; } public int getEmpno() { return empno; } public String getEname() { return ename; } public String getJob() { return job; } public double getSal() { return sal; } public double getComm() { return comm; } public String getInfo() { return "雇员编号:" + empno + "\n" + "雇员姓名:" + ename + "\n" + "雇员职位:" + job + "\n" + "基本工资:" + sal + "\n" + "佣金:" + comm; } }
范例:编写测试程序
public class Demo { public static void main(String args[]) { Emp e = new Emp(7899, "SMITH", "CLERK", 800.0, 1.0); e.setEname("ALLEN"); System.out.println(e.getInfo()); System.out.println("姓名:" + e.getEname()); } }
所有类之中提供的setter、getter方法可能某些操作之中不会使用到,但是依然必须提供。
所有的setter方法除了具备有设置属性的内容之外,也具备有修改属性内容的功能。