在实例方法或构造器中,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的可读性。不过,通常我们都习惯省略this。
但是,当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加this来表明该变量是类的成员变量。即:我们可以用this来区分成员变量
和局部变量
。比如:
另外,使用this访问属性和方法时,如果在本类中未找到,会从父类中查找。这个在继承中会讲到。
this可以作为一个类中构造器相互调用的特殊格式。
this()
:调用本类的无参构造器this(实参列表)
:调用本类的有参构造器注意:
this(形参列表)
"this()
和this(实参列表)
只能声明在构造器首行。
this(参数列表)
"目前可能出现的问题?及解决方案?
我们在声明一个属性对应的setXxx
方法时,通过形参给对应的属性赋值。如果形参名和属性名同名了,那么该如何在方法内区分这两个变量呢?
解决方案:使用this。具体来讲,使用this修饰的变量,表示的是属性。没有用this修饰的,表示的是形参。
【举例】
看下面的代码:
public class PersonTest {
}
class Person{
//属性
String name;
int age;
//方法
public void setAge(int a){
age=a;
}
}
可以进一步在main方法里面输出看一下结果:
public class PersonTest {
public static void main(String[] args) {
Person p1=new Person();
p1.setAge(10);
System.out.println(p1.age);
}
}
class Person{
//属性
String name;
int age;
//方法
public void setAge(int a){
age=a;
}
}
我们知道,定义变量的时候,变量名其实是一个标识符,定义标识符要求“见名知意”。但是这里的形参a与age没什么联系。
构造器里面也一样,这样写过于抽象,如下:
class Person{
//属性
String name;
int age;
String email;
//构造器
public Person(){
}
public Person(String n,String a){
name=n;
email=e;
}
//方法
public void setAge(int a){
age=a;
}
}
这里鼓励大家,把形参名写的与属性名一致。这样更加明了,不会变得很抽象。
但是这样来写又出问题了:(注意看方法)
public class PersonTest {
public static void main(String[] args) {
Person p1=new Person();
p1.setAge(10);
System.out.println(p1.age);
}
}
class Person{
//属性
String name;
int age;
//方法
public void setAge(int age){
age=age;
}
}
可以看到,setAge方法里面得age分不清是什么了,是形参还是属性?
这里有一个就近原则,setAge方法里面的age都是形参。
运行结果可以看到并没有赋上值:
解决方法很简单,只需要在想表示为属性的变量前面加上this.
即可。
就像这样:
//方法
public void setAge(int age){
this.age=age;
}
再次调用一下试试:
public class PersonTest {
public static void main(String[] args) {
Person p1=new Person();
p1.setAge(10);
System.out.println(p1.age);
}
}
class Person{
//属性
String name;
int age;
//方法
public void setAge(int age){
this.age=age;
}
}
上面的场景下,this属于必须要用的。(区分属性和形参)
如果要写get方法:
public int getAge(){
return this.age;
}
这里的getAge
方法并没有形参,所以下面的this.age
的this.
可加可不加,age
肯定是属性。
但是刚才的setAge
方法里面,this.
是必须要加上的,因为有了形参,加上this以示区分形参与属性。
再看这段代码:
public class PersonTest {
public static void main(String[] args) {
Person p1=new Person();
p1.setAge(10);
System.out.println(p1.age);
}
}
class Person{
//属性
String name;
int age;
//方法
public void setAge(int age){
this.age=age;
}
}
这里的this可以理解为:当前属性所属的对象。
这个案例中,main方法里:p1.setAge(10);
我们拿p1调用的setAge。这里的p1就充当了setAge方法里面的this
。
当然,setAge方法里面的this不能写成p1,因为是先创建类,再创建对象。而且根本事先不知道对象是谁,这里肯定是需要动态的表示对象。这里所谓的动态,就是this来表示的。
这个this,就表示当前对象,谁调用setAge方法,这个this对象就是谁。比如刚才调用p1.setAge(10);
那么p1就是this。
方法里面可以调属性,谁调用这个方法,这个属性就是谁的。
构造器一致。如下:
public class PersonTest {
public static void main(String[] args) {
Person p2=new Person("Tom","[email protected]");//声明完之后,也能给属性赋值
System.out.println("name="+p2.name+",email="+p2.email);
}
}
class Person{
//属性
String name;
int age;
String email;
//构造器
public Person(){
}
public Person(String name,String email){
this.name=name;
this.email=email;
}
}
这里的this理解为:当前正在创建的对象。在当前案例中,this就是执行完之后的p2。
输出结果为:
【针对于方法内的使用情况】(准确的说是非static修饰的方法)
this理解为:当前对象
1.一般情况:我们通过对象a调用方法,可以在方法内调用当前对象a的属性或其他方法。
此时,我们可以在属性和其他方法前使用"this.
",表示当前属性或方法所属的对象a。但是,一般情况下,我们都选择省略此"this.
"结构。(没有局部变量)
2.特殊情况:如果方法的形参与对象的属性同名了,我们必须使用"this.
"进行区分。
使用this.
修饰的变量即为属性(或成员变量),没有使用this.
修饰的变量,即为局部变量。
【针对于构造器内的使用情况】
this理解为:当前正在创建的对象
1.一般情况:我们通过构造器创建对象时,可以在构造器内调用当前正在创建的对象的属性或方法。
此时,我们可以在属性和方法前使用"this.
",表示当前属性或方法所属的对象。但是,一般情况下,我们都选择省略此"this.
"结构。
2.特殊情况:如果构造器的形参与正在创建的对象的属性同名了,我们必须使用"this.
"进行区分。
使用this.
修饰的变量即为属性(或成员变量),没有使用this.
修饰的变量,即为局部变量。
【案例】
疑问
看下面代码:
class User{
//属性
String name;
int age;
//构造器
public User(){
//模拟对象创建时,需要初始化50行代码
}
public User(String name){
//模拟对象创建时,需要初始化50行代码
this.name=name;
}
public User(String name,int age){
//模拟对象创建时,需要初始化50行代码
this.name=name;
this.age=age;
}
}
如果在模拟对象创建时,需要初始化50行代码,而这50行代码每一个构造器都要加载。这怎么办呢?
首先想到的可能是定义一个方法,将50行代码写进去,然后再在构造器里面调用即可,比如init()
方法:
class User{
//属性
String name;
int age;
//构造器
public User(){
init();
}
public User(String name){
init();
this.name=name;
}
public User(String name,int age){
init();
this.name=name;
this.age=age;
}
//方法
private void init(){
//模拟对象创建时,需要初始化50行代码
//...
}
}
但是如果现在的构造器代码是这样的:
public User(String name){
//模拟对象创建时,需要初始化30行代码
this.name=name;
//模拟对象创建时,需要初始化20行代码(这里面的代码又用到了name)
}
那么就可能需要定义好几个方法,会显得很复杂。
解决
针对上面的疑问,可以这样来解决。
将这50行代码放在User()
里面,后边的构造器直接调用它。
class User{
//属性
String name;
int age;
//构造器
public User(){
//模拟对象创建时,需要初始化50行代码
//...
}
public User(String name){
this(); //模拟对象创建时,需要初始化50行代码
this.name=name;
}
public User(String name,int age){
this(); //模拟对象创建时,需要初始化50行代码
this.name=name;
this.age=age;
}
}
使用this()表示调用的是当前空参的构造器,不要写成User()了,只是规定!!!
代码:
class User{
//属性
String name;
int age;
//构造器
public User(){
//模拟对象创建时,需要初始化50行代码
//...
}
public User(String name){
this(); //模拟对象创建时,需要初始化50行代码
this.name=name;
}
public User(String name,int age){
this(name); //模拟对象创建时,需要初始化50行代码
//this.name=name;
this.age=age;
}
}
问:既然执行User(String name,int age)
方法中的this(name);
语句,需要调用User(String name)
和User()
,那么会创建三个对象么?
不管在构造器里面直接/间接调用了几个重载的构造器,这里创建的对象只有一个。
之前说过构造器的作用之一是:搭配new关键字,创建类的对象。
在构造器里面互相调用并没有用到new关键字,更不用谈创建对象了。
代码:
public class UserTest {
//只创建了User类的1个对象
User u1=new User("Tom",14);
}
class User{
//属性
String name;
int age;
//构造器
public User(){
//模拟对象创建时,需要初始化50行代码
//...
}
public User(String name){
this(); //模拟对象创建时,需要初始化50行代码
this.name=name;
}
public User(String name,int age){
this(name); //模拟对象创建时,需要初始化50行代码
this.age=age;
}
}
总结
1.格式:“this(形参列表)
” —>形参列表可以是空的
我们可以在类的构造器中,调用当前类中指定的其它构造器。
2.要求:"this(形参列表)
"必须声明在当前构造器的首行
3.结论:"this(形参列表)
"在构造器中最多声明一个
4.如果一个类中声明了n个构造器,则最多有n-1个构造器可以声明有"this(形参列表)
"的结构。
上边已经说了自己不能调自己,那调用别人呢?
比如,在User()
构造器中调用User(String name,int age)
构造器:this("Tom",20);
如下:
public User(){
this("Tom",20);
//模拟对象创建时,需要初始化50行代码
//...
}
public User(String name){
this(); //模拟对象创建时,需要初始化50行代码
this.name=name;
}
public User(String name,int age){
this(name); //模拟对象创建时,需要初始化50行代码
this.age=age;
}
可以互相调用,但不能导致闭环,要不然会死循环。
1.this
可以调用的结构:成员变量、方法、**构造器 **(不能是局部变量)
①成员变量
public void setAge(int age){
this.age=age;
}
②方法
方法里面可以调用方法,如下面的sleep();
public void eat(){
System.out.println("人吃饭");
sleep();
}
public void sleep(){
System.out.println("人睡觉");
}
其实,sleep();
前面可以加上this.
:
public void eat(){
System.out.println("人吃饭");
this.sleep();
}
public void sleep(){
System.out.println("人睡觉");
}
谁调用eat方法,就拿这个对象去调用sleep();之前没有写不代表没有this,只是省略了而已。
2. this
的理解:当前对象(在方法中调用时) 或 当前正在创建的对象(在构造器中调用时)
题目描述
根据图示,添加必要的构造器,综合应用构造器的重载,this关键字。
代码
【Boy.java】
package yuyi01;
/**
* ClassName: Boy
* Package: yuyi01
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 16:02
*/
public class Boy {
//属性
private String name;
private int age;
//方法
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name; //此this可以省略
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return this.age; //此this可以省略
}
public void marry(Girl girl){
System.out.println("我想娶"+girl.getName());
}
public void shout(){
if(this.age>=22){ //此this可以省略
System.out.println("我终于可以带喜欢的人回家了");
}else{
System.out.println("我还要更加努力变得优秀");
}
}
//构造器
public Boy() {
}
public Boy(String name, int age) {
this.name = name;
this.age = age;
}
}
【Girl.java】
package yuyi01;
/**
* ClassName: Girl
* Package: yuyi01
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 16:11
*/
public class Girl {
//属性
private String name;
private int age;
//方法
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name; //此this可以省略
}
public void marry(Boy boy){
System.out.println("我想嫁给"+boy.getName());
boy.marry(this);
}
/**
* 比较两个Girl对象的大小--调compare方法的girl和形参girl
* @param girl--待比较的对象
* @return 若返回值为正数,则表示当前对象大;若为负数,则表示当前对象小(或形参girl大);若为0,则表示两者相等
*/
public int compare(Girl girl){
if(this.age>girl.age){ //如果当前对象的age大于形参对象的age,返回正数
return 1;
}else if(this.age<girl.age){ //如果当前对象的age小于形参对象的age,返回负数
return -1;
}else{ //如果当前对象的age等于形参对象的age,返回0
return 0;
}
}
//构造器
public Girl() {
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
}
【BoyGirlTest.java】
package yuyi01;
/**
* ClassName: BoyGirlTest
* Package: yuyi01
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 17:06
*/
public class BoyGirlTest {
public static void main(String[] args) {
Boy boy1=new Boy("Jack",24);
Girl girl1=new Girl("Luis",22);
girl1.marry(boy1);
boy1.shout();
Girl girl2=new Girl("Cendy",20);
int compare=girl1.compare(girl2);
if(compare>0){
System.out.println(girl1.getName()+"大");
} else if (compare<0) {
System.out.println(girl2.getName()+"大");
}else{
System.out.println(girl1.getName()+girl2.getName()+"一样大");
}
}
}
注意
【一个易错细节】
在Girl.java里面的marry方法
,我们调用了boy.marry,里面的参数有可能会写错。
这里女孩说想嫁给男孩,男孩回应也想娶这个女孩。第二个图片是正确的:
在main方法里面执行marry方法:
【compare方法】
比较两个对象的大小,其实比较的是两个属性的大小。
/**
* 比较两个Girl对象的大小--调compare方法的girl和形参girl
* @param girl--待比较的对象
* @return 若返回值为正数,则表示当前对象大;若为负数,则表示当前对象小(或形参girl大);若为0,则表示两者相等
*/
public int compare(Girl girl){
if(this.age>girl.age){ //如果当前对象的age大于形参对象的age,返回正数
return 1;
}else if(this.age<girl.age){ //如果当前对象的age小于形参对象的age,返回负数
return -1;
}else{ //如果当前对象的age等于形参对象的age,返回0
return 0;
}
}
【Tips1】一键生成get/set方法
比如现在只写了属性:
public class hello {
private int apple;
private int coffee;
}
然后想要生成get与set方法。
键盘同时按住Alt+Insert
键,然后选择下面红框部分:
然后按住Crtl键,将两个都选中:
确定之后就可以自动生成了:
public class hello {
private int apple;
private int coffee;
public int getApple() {
return apple;
}
public void setApple(int apple) {
this.apple = apple;
}
public int getCoffee() {
return coffee;
}
public void setCoffee(int coffee) {
this.coffee = coffee;
}
}
【Tips2】一键生成构造器
现在我们想要生成构造器,还是同样键盘同时按住Alt+Insert
键,然后选择下面红框部分:
如果只想生成空参构造器,那么点这个即可:
生成:
public hello() {
}
若要生成带参构造器,按住Ctrl键将它们都选中,然后确定即可:(根据需要选择参数)
生成:
public hello(int apple, int coffee) {
this.apple = apple;
this.coffee = coffee;
}
idea没有那么智能,它前后参数的顺序不是我们刚才点击的顺序,而是属性定义先后的顺序。
题目描述
1、按照UML类图,创建Account类,提供必要的结构。
2、按照UML类图,创建Customer类,提供必要的结构。
3、按照UML类图,创建Bank类,提供必要的结构。
4、创建BankTest类,进行测试。
代码
【Account.java】
package yuyi02;
/**
* ClassName: Account
* Package: yuyi01
* Description:
* 账户类
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 17:20
*/
public class Account {
//属性
private double balance; //余额
//方法
public double getBalance(){
return balance;
}
public void deposit(double amt){ //存钱
if(amt>0){
balance+=amt;
System.out.println("成功存入"+amt);
}
}
public void withdraw(double amt){ //取钱
if(balance>=amt && amt>0){
balance-=amt;
System.out.println("成功取出"+amt);
}else{
System.out.println("取款数额有误或余额不足");
}
}
//构造器
public Account(double init_balance){
this.balance=init_balance;
}
}
【Customer.java】
package yuyi02;
/**
* ClassName: Customer
* Package: yuyi01
* Description:
* 客户类
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 17:20
*/
public class Customer {
//属性
private String firstName; //名
private String lastName; //姓
private Account account; //账户
//方法
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
//构造器
public Customer(String f, String l) {
this.firstName = f;
this.lastName = l;
}
}
【Bank.java】
package yuyi02;
/**
* ClassName: Bank
* Package: yuyi02
* Description:
* 银行类
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 17:21
*/
public class Bank {
//属性
private Customer[] customers; //用于保存多个客户
private int numberOfCustomer; //用于记录存储的客户的个数
//构造器
public Bank(){ //创建对象的时候就可以把数组创建好
//数组赋值
customers=new Customer[10]; //还可以在Bank里面写一个参数,将参数赋值给Customer[],每次造对象的时候就可以指明一下数组的长度
}
//方法
/**
* 将指定姓名的客户保存在银行的客户列表中
* @param f
* @param l
*/
public void addCustomer(String f,String l){
//把f和l为代表的姓和名封装为一个客户对象
Customer cust=new Customer(f,l);
//然后把对象放到数组里面
customers[numberOfCustomer]=cust; //将创建的对象的地址赋值给customers数组的第numberOfCustomer的位置
//每调用一次方法,就会记录一次客户的数量(加1)
numberOfCustomer++;
}
/**
* 获取客户列表中存储的客户个数
* @return
*/
public int getNumOfCustomers(){
return numberOfCustomer;
}
/**
* 获取指定索引位置上的客户
* @param index
* @return
*/
public Customer getCustomer(int index){
//先写有效/无效范围都行
if(index<0 || index>=numberOfCustomer){ //无效范围 ,若有3个数,最大角标就是2,这里可以取等号
return null;
}else{
return customers[index];
}
}
}
【BankTest.java】
package yuyi02;
/**
* ClassName: BankTest
* Package: yuyi02
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/10/27 0027 17:21
*/
public class BankTest {
public static void main(String[] args) {
Bank bank=new Bank();
//添加两个客户
bank.addCustomer("飞","张");
bank.addCustomer("备","刘");
//获取客户名字(从数组中通过索引号获取),然后通过setAccount来new一个账户,并且让初始余额为1000块钱
bank.getCustomer(0).setAccount(new Account(1000));
//获取客户名字(从数组中通过索引号获取),然后通过getAccount获取它的账户,然后取出50块钱
bank.getCustomer(0).getAccount().withdraw(50);
System.out.println("账户余额为: "+bank.getCustomer(0).getAccount().getBalance());//查看余额
}
}
注意
1.【addCustomer方法】
public class Bank {
//属性
private Customer[] customers; //用于保存多个客户
private int numberOfCustomer; //用于记录存储的客户的个数
//方法
/**
* 将指定姓名的客户保存在银行的客户列表中
* @param f
* @param l
*/
public void addCustomer(String f,String l){
//把f和l为代表的姓和名封装为一个客户对象
Customer cust=new Customer(f,l);
//然后把对象放到数组里面
customers[numberOfCustomer]=cust;
//每调用一次方法,就会记录一次客户的数量(加1)
numberOfCustomer++;
}
}
2.【index有效范围】
/**
* 获取指定索引位置上的客户
* @param index
* @return
*/
public Customer getCustomer(int index){
//先写有效/无效范围都行
if(index<0 || index>=numberOfCustomer){ //无效范围 ,若有3个数,最大角标就是2,这里可以取等号
return null;
}else{
return customers[index];
}
}
if结构还可以这样来写,减少嵌套:
/**
* 获取指定索引位置上的客户
* @param index
* @return
*/
public Customer getCustomer(int index){
//先写有效/无效范围都行
if(index<0 || index>=numberOfCustomer){ //无效范围 ,若有3个数,最大角标就是2,这里可以取等号
return null;
}
return customers[index];
}
3.【难点代码】
BankTest测试类里面,创建好两个对象之后:
Bank bank=new Bank();
//添加两个客户
bank.addCustomer("飞","张");
bank.addCustomer("备","刘");
①
//获取客户名字(从数组中通过索引号获取),然后通过setAccount来new一个账户,
bank.getCustomer(0).setAccount(new Account(1000));
②
//获取客户名字(从数组中通过索引号获取),然后通过getAccount获取它的账户,然后取出50块钱
bank.getCustomer(0).getAccount().withdraw(50);
③
System.out.println("账户余额为: "+bank.getCustomer(0).getAccount().getBalance());//查看余额
4.【内存结构剖析】
核心代码:
public class BankTest {
public static void main(String[] args) {
Bank bank=new Bank();
//添加两个客户
bank.addCustomer("飞","张");
bank.addCustomer("备","刘");
//获取客户名字(从数组中通过索引号获取),然后通过setAccount来new一个账户,并且让初始余额为1000块钱
bank.getCustomer(0).setAccount(new Account(1000));
//获取客户名字(从数组中通过索引号获取),然后通过getAccount获取它的账户,然后取出50块钱
bank.getCustomer(0).getAccount().withdraw(50);
System.out.println("账户余额为: "+bank.getCustomer(0).getAccount().getBalance());//查看余额
}
}