this关键字
this可以算是Java里面比较复杂的关键字,因为this的使用形式上决定了它的灵活性,在程序里面使用this可以实现以下三类结构的描述:
1.当前类中的属性:this.属性;
2.当前类中的方法(构造方法、普通方法):this()、this.方法名称();
3.描述当前对象;
this调用本类属性(使用this调用当前类中属性)
通过现在的分析可以发现,利用构造方法或者是setter方法都可以进行类中的属性的赋值,但是在进行赋值的时候,之前采用的是如下的定义形式:
class Person {
private String name ;
private int age ;
public Person(String n,int a) {
name = n ;
age = a ;
}
public void tell() {
System.out.println("姓名:" + name + "、年龄:" + age) ;
}
// setter、getter略
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc",39) ;
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
姓名:lhc、年龄:39
但是这个时候在构造方法定义的过程之中会发现有一点点的问题:
public Person(String n,int a) {
name = n ;
age = a ;
}
这个问题出现在参数名称上,可以发现此时构造方法中两个参数的目的是为了类中的name或age属性初始化,但是现在却发现此时的代码n和a参数名称不好。如果说现在将构造方法中的参数名称修改为name、age,则发现无法进行属性的正确设置:
public Person(String name,int age) {
name = name ;
age = age ;
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
姓名:null、年龄:0
在Java程序之中,“{}”是作为一个结构体的边界符,那么在程序里面当进行变量(参数、属性都称为变量)使用的时候都会以“{}”作为一个查找边界,所以按照就近取用的原则,此时的构造方法并没有能够访问类中的属性,所以此时为了明确标记出类中的属性与参数的区别,往往会在属性前追加一个“this”,表示本类属性。
class Person {
private String name ;
private int age ;
public Person(String name,int age) {
this.name = name ;
this.age = age ;
}
public void tell() {
System.out.println("姓名:" + name + "、年龄:" + age) ;
}
// setter、getter略
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc",39) ;
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
姓名:lhc、年龄:39
在你以后所编写的程序代码之中,只要是访问本类中属性的时候,请一定要加上“this”实现访问。
class Person {
private String name ;
private int age ;
public Person(String name,int age) {
this.name = name ;
this.age = age ;
}
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
// setter、getter略
}
以前吃过亏,所以 只要是访问本类中属性的时候,请一定要加上“this”实现访问
this调用本类方法
除了调用属性之外,this也可以实现方法的调用,但是对于方法的调用就必须考虑构造与普通方法。
1.构造方法调用(this()):使用关键字new实例化对象的时候才会调用构造方法;
2.普通方法调用(this.方法名称()):实例化对象产生之后就可以调用普通方法。
范例:调用类中的普通方法
class Person {
private String name ;
private int age ;
public Person(String name,int age) {
this.setName(name) ;
setAge(age) ; // 加与不加都表示本类方法
}
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
public void setName(String name) {
this.name = name ;
}
public void setAge(int age) {
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge() {
return this.age ;
}
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc",39) ;
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
姓名:lhc、年龄:39
除了普通的方法调用之外,还需要进行构造方法的调用,对于构造方法的调用,肯定是要放在构造方法中执行。现在假设说类中一共定义有三个构造方法,但是要求不管调用那个构造方法,都执行一行输出语句“一个新的Person类的对象实例化”。
传统做法实现:
class Person {
private String name ;
private int age ;
public Person() {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
}
public Person(String name) {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
this.name = name ;
}
public Person(String name,int age) {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
this.name = name ;
this.age = age ;
}
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
// setter、getter略
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc",39) ;
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
*** 一个新的Person类对象实例化了。****
姓名:lhc、年龄:39
D:\fgqjava>
把主类中的年龄参数39去掉,只剩单参,代码修改处如下:
class Person {
private String name ;
private int age ;
public Person() {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
}
public Person(String name) {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
this.name = name ;
}
public Person(String name,int age) {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
this.name = name ;
this.age = age ;
}
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
// setter、getter略
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc") ; //修改之处,去掉年龄参数39,只剩单参
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
*** 一个新的Person类对象实例化了。****
姓名:lhc、年龄:0
D:\fgqjava>
System.out.println("*** 一个新的Person类对象实例化了。****") ;
this.name = name ;
把上面的代码想象成10行代码,则会出现代码重复。
如果要想评价一个代码的好坏:
代码结构可以重用,提供的是一个中间独立的支持
我们的目标是:没有重复代码。
利用 this() 构造调用优化
class Person {
private String name ;
private int age ;
public Person() {
System.out.println("*** 一个新的Person类对象实例化了。****") ;
}
public Person(String name) {
this() ; // 调用本类无参构造
this.name = name ;
}
public Person(String name,int age) {
this(name) ; // 调用本类单参构造
this.age = age ;
}
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
// setter、getter略
}
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc") ; // 使用单参
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
*** 一个新的Person类对象实例化了。****
姓名:lhc、年龄:0
代码与传统方法比较,代码减少了很多
主类中使用多参:
... ...
public class JavaDemo {
public static void main(String args[]) {
Person per = new Person("lhc",10) ; // 使用多参
per.tell() ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
*** 一个新的Person类对象实例化了。****
姓名:lhc、年龄:10
对于本类构造方法的互相调用需要注意以下几点重要问题:
1.构造方法必须在实例化新对象的时候调用,所以“this()”的语句只允许放在构造方法的首行。
2.构造方法互相调用时请保留有程序的出口,别形成死循环。
分析问题1
原来:
public Person(String name) {
this() ; // 调用本类无参构造
this.name = name ;
}
不放在首行,代码如下,会报错:
public Person(String name) {
this.name = name ;
this() ; // 调用本类无参构造
}
原来的普通方法:
public void tell() {
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
修改为如下代码,会报错:
public void tell() {
this() ; // 调用本类无参构造
System.out.println("姓名:" + this.name + "、年龄:" + this.age) ;
}
“this()”的语句必须且只能放在构造方法的首行,不能放在普通方法中。
分析问题2
public Person() {
this("haha",11) ; // 构造方法,且是首行,但是
System.out.println("*** 一个新的Person类对象实例化了。****") ;
}
public Person(String name) {
this() ; // 调用本类无参构造
this.name = name ;
}
public Person(String name,int age) {
this(name) ; // 调用本类单参构造
this.age = age ;
}
双参调用单参,单参调用无参,无参里面有双参,死循环。
此时的程序在进行编译的时候将会直接出现错误提示:告诉用户,你出现了构造方法的递归调用。
构造方法互调用案例:
现在要求定义一个描述有员工信息的程序类,该类中提供有:编号、姓名、部门、工资,在这个类之中提供有四个构造方法:
【无参构造】编号定义为1000,姓名定义为无名氏;
【单参构造】传递编号,姓名定义为“新员工”,部门定义为“未定”,工资为0.0;
【三参构造】传递编号、姓名、部门,工资为2500.00;
【四参构造】所有的属性全部进行传递。
范例:进行代码的初期实现
class Emp {
private long empno ; // 员工编号
private String ename ; // 员工姓名
private String dept ; // 部门名称
private double salary ; // 基本工资
public Emp() {
this.empno = 1000 ;
this.ename = "无名氏" ;
}
public Emp(long empno) {
this.empno = empno ;
this.ename = "新员工" ;
this.dept = "未定" ;
}
public Emp(long empno,String ename,String dept) {
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = 2500.00 ;
}
public Emp(long empno,String ename,String dept,double salary) {
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = salary ;
}
// setter、getter略
public String getInfo() { // 给出一个提示信息
return "雇员编号:" + this.empno +
"、雇员姓名:" + this.ename +
"、所在部门:" + this.dept +
"、基本工资:" + this.salary;
}
}
public class JavaDemo {
public static void main(String args[]) {
Emp emp = new Emp(7369L,"史密斯","财务部",6500.00) ;
System.out.println(emp.getInfo()) ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
雇员编号:7369、雇员姓名:史密斯、所在部门:财务部、基本工资:6500.0
此时可以发现代码有重复,所以就可以对Emp类进行简化定义。
主类中调用三参构造:7369L,"史密斯","财务部"
class Emp {
private long empno ; // 员工编号
private String ename ; // 员工姓名
private String dept ; // 部门名称
private double salary ; // 基本工资
public Emp() {
this(1000,"无名氏",null,0.0) ;
}
public Emp(long empno) {
this(empno,"新员工","未定",0.0) ;
}
public Emp(long empno,String ename,String dept) {
this(empno,ename,dept,2500.00) ;
}
public Emp(long empno,String ename,String dept,double salary) {
this.empno = empno ;
this.ename = ename ;
this.dept = dept ;
this.salary = salary ;
}
// setter、getter略
public String getInfo() { // 给出一个提示信息
return "雇员编号:" + this.empno +
"、雇员姓名:" + this.ename +
"、所在部门:" + this.dept +
"、基本工资:" + this.salary;
}
}
public class JavaDemo {
public static void main(String args[]) {
Emp emp = new Emp(7369L,"史密斯","财务部") ;
System.out.println(emp.getInfo()) ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
雇员编号:7369、雇员姓名:史密斯、所在部门:财务部、基本工资:2500.0
因为调用的是3参,所以显示salary为2500.00,而不是6500.00
代码的任何位置上都可能有重复,所以消除重复的代码是先期学习中最需要考虑的部分。
综合实战:简单Java类
在以后进行项目的开发与设计的过程之中,简单Java类都将作为一个重要的组成部分存在,慢慢接触到正规的项目设计之后,简单Java类无处不在,并且有可能会产生一系列的变化。所谓的简单Java类指的是可以描述某一类信息的程序类,例如:描述一个人、描述一本书、描述一个部门、描述一个雇员,并且在这个类之中并没有特别复杂的逻辑操作,只作为一种信息存储的媒介存在。
对于简单Java类而言,其核心的开发结构如下:
1.类名称一定要有意义,可以明确的描述某一类事务;
2.类之中的所有属性都必须使用private进行封装,同时封装后的属性必须要提供有setter、getter方法;此处的“必须提供”指的是简单Java类,并非所有的类。
3.类之中可以提供有无数多个构造方法,但是必须要保留有无参构造方法;
4.类之中不允许出现任何的输出语句,所有内容的获取必须返回;
5.【非必须】可以提供有一个获取对象详细信息的方法,暂时将此方法名称定义为getInfo()。
范例:定义一个简单Java类
class Dept { // 类名称可以明确描述出某类事物
private long deptno ;
private String dname ;
private String loc ;
public Dept() {} // 必须提供有“无参”
public Dept(long deptno,String dname,String loc) { // 有参构造可以有很多个
this.deptno = deptno ;
this.dname = dname ;
this.loc = loc ;
}
public String getInfo() { // 输出信息,非必须的
return "【部门信息】部门编号:" + this.deptno + "、部门名称:" + this.dname + "、部门位置:" + this.loc ;
}
public void setDeptno(long deptno) {
this.deptno = deptno ;
}
public void setDname(String dname) {
this.dname = dname ;
}
public void setLoc(String loc) {
this.loc = loc ;
}
public long getDeptno() {
return this.deptno ;
}
public String getDname() {
return this.dname ;
}
public String getLoc() {
return this.loc ;
}
}
public class JavaDemo {
public static void main(String args[]) {
Dept dept = new Dept(10,"技术部","北京") ;
System.out.println(dept.getInfo()) ;
}
}
D:\fgqjava>javac JavaDemo.java
D:\fgqjava>java JavaDemo
【部门信息】部门编号:10、部门名称:技术部、部门位置:北京
D:\fgqjava>
这种简单Java类基本上就融合了所有的现在接触到的概念,例如:数据类型划分、类的定义、private封装、构造方法、方法定义、对象实例化。简单Java类是很容易理解的,但是很重要,别到时候写不出来。
衍生出的问题:后续解决
关键字new干了什么事情?
GC如何做的?
为什么有setter和getter?
为什么有无参构造方法?