深入分析类与对象

一、封装性

在研究封装之前,首先先观察如下一段代码:

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方法除了具备有设置属性的内容之外,也具备有修改属性内容的功能。

猜你喜欢

转载自blog.csdn.net/l_zhai/article/details/81194578