目录
🏀this关键字
🥅this的内存结构
⭐️this是一个关键字,全部小写。
⭐️this是什么,在内存方面是怎样的?
(1) 一个对象一个this。
(2)this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。
(3)严格意义上来说,this代表的就是“当前对象”;this存储在堆内存当中对象的内部。⭐️this只能使用在实例方法中。谁调用这个实例方法,this就是谁。
所以this代表的是:当前对象。⭐️“this.”大部分情况下是可以省略的。
⭐️为什么this不能使用在静态方法(static)中?
this代表当前对象,静态方法中不存在当前对象。
❤️例1
public class ThisTest01{
public static void main(String[] args){
//创建一个对象,并初始化为"张三"
Customer c1 = new Customer("张三");
c1.shopping();
//创建另一个对象,并初始化为"李四"
Customer c2 = new Customer("李四");
c2.shopping();
//static静态方法用类名调用,不需要this
Customer.doSome();
}
}
// 顾客类
class Customer{
//1. 属性
// 实例变量(必须采用“引用.”的方式访问)
String name;
// 构造方法,用来创建对象和给变量初始化的
public Customer(){ //无参构造方法
}
public Customer(String s){ //有参构造方法
name = s;
}
//3. 顾客购物的方法
// 实例方法
public void shopping(){
// 这里的this是谁?this是当前对象。
// c1调用shopping(),this是c1
// c2调用shopping(),this是c2
//System.out.println(this.name + "正在购物!");
// this. 是可以省略的。
// this. 省略的话,还是默认访问“当前对象”的name。
//实例变量我们必须用“引用.”来访问;这里实际上省略了this.
System.out.println(name + "正在购物!");
}
//3. 静态方法
public static void doSome(){
// this代表的是当前对象,而静态方法的调用不需要对象。矛盾了。
// 错误: 无法从静态上下文中引用非静态 变量 this
//System.out.println(this);
}
}
// ------------------------------补充:
class Student{
// 实例变量,怎么访问?必须先new对象,通过“引用.”来访问。
String name = "zhangsan";
// 静态方法
public static void m1(){
//System.out.println(name);
// this代表的是当前对象----------这还没有创建对象
// System.out.println(this.name);
// 除非你这样------创建对象
Student s = new Student();
System.out.println(s.name);
}
// 为什么set和get方法是实例方法?
public void setName(String s){
name = s;
}
public String getName(){
return name;//省略了this.实际上是this.name
}
// 又回到上午的问题了?什么时候方法定义为实例方法,什么时候定义为静态方法?
// 如果方法中直接访问了实例变量,该方法必须是实例方法。
}
⭐️内存图
❤️例2
public class ThisTest02{
//1. 实例变量,必须先new对象才能访问,采用“引用.”
int i = 100;
//2. 静态变量,使用"类名."就可以访问;且类名可以省略
static int k = 111;
// 静态方法
public static void main(String[] args){
// 错误: 无法从静态上下文中引用非静态 变量 i
// System.out.println(i);
// 怎么样访问i
ThisTest02 tt = new ThisTest02();
System.out.println(tt.i);
// 静态变量用“类名.”访问。
System.out.println(ThisTest02.k);
// 类名. 能省略
System.out.println(k);
}
}
🥅this什么时候不能省
❤️例1
⭐️this可以使用在实例方法中,不能使用在静态方法中。
⭐️this关键字大部分情况下可以省略,什么时候不能省略呢?
在实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下:this. 是不能省略的。
public class ThisTest03{
public static void main(String[] args){
//创建对象,调用无参构造方法
Student s = new Student();
s.setNo(111);
s.setName("张三");
System.out.println("学号:" + s.getNo());
System.out.println("姓名:" + s.getName());
//创建对象,调用有参构造方法
Student s2 = new Student(2222, "李四");
System.out.println("学号:" + s2.getNo());
System.out.println("姓名:" + s2.getName());
}
}
// 学生类
class Student{
private int no;
private String name;
//构造方法无参
public Student(){
}
//构造方法有参,思考一下下面的代码弊端?方法中的形参没有实际意义,不能增强可读性
public Student(int i, String s){
no = i;
name = s;
}
/*
//为了增强可读性,进行修改;但是此时又会出现问题
//根据就近原则,下面的no和name都是局部变量,和实例变量没关系!
public Student(int no, String name){ //都是这里的no和name
no = no;
name = name;
}
*/
// 再次更改上面的构造方法也增强以下可读性
public Student(int no, String name){
this.no = no;
this.name = name; //此时加上this.就可以区分局部变量和实例变量!且不能省略!
}
// setter and getter方法---------也可以进行更改
public void setNo(int i){
no = i;
}
public int getNo(){
return no; // 这里也有this.但是可以省略!this.no当前对象的no!
//return this.no;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
}
🥅this()的用法
⭐️this除了可以使用在实例方法中,还可以用在构造方法(无参构造调用有参构造)中。
⭐️新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:
this(实际参数列表);通过一个构造方法1去调用构造方法2,可以做到代码复用。
但需要注意的是:“构造方法1”和“构造方法2” 都是在同一个类当中。最常用的就是无参构造调用有参构造!⭐️this() 这个语法作用是什么? 代码复用。
⭐️死记硬背:对于this()的调用只能出现在构造方法的第一行。
❤️例1
/*
需求:
1、定义一个日期类,可以表示年月日信息。
2、需求中要求:
如果调用无参数构造方法,默认创建的日期为:1970年1月1日。
当然,除了调用无参数构造方法之外,也可以调用有参数的构造方法来创建日期对象。
*/
public class ThisTest04{
public static void main(String[] args){
// 调用无参数构造方法
Date d1 = new Date();
d1.detail();
// 调用有参数构造方法
Date d2 = new Date(2008, 8, 8);
d2.detail();
}
}
class Date{ // 以后写代码都要封装,属性私有化,对外提供setter and getter
private int year;
private int month;
private int day;
// 构造方法无参
// 调用无参数构造方法,初始化的日期是固定值。
public Date(){
System.out.println(11);//错误: 对this()的调用必须是构造器中的第一个语句
/*
this.year = 1970;
this.month = 1;
this.day = 1;
*/
this(1970, 1, 1);
//this只能出现在第一行!利用this从一个构造方法调用另一个构造方法;进行代码复用
}
// 构造方法有参数
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
// 提供一个可以打印日期的方法
public void detail(){
//System.out.println(year + "年" + month + "月" + day + "日");
System.out.println(this.year + "年" + this.month + "月" + this.day + "日");
}
//setter and getter
public void setYear(int year){
// 设立关卡(有时间可以设立关卡)
this.year = year;
}
public int getYear(){
return year;
}
public void setMonth(int month){
// 设立关卡(有时间可以设立关卡)
this.month = month;
}
public int getMonth(){
return month;
}
public void setDay(int day){
// 设立关卡(有时间可以设立关卡)
this.day = day;
}
public int getDay(){
return day;
}
}
❤️this小总结
⭐️this是一个关键字,是一个引用,保存内存地址指向自身。
⭐️this可以使用在实例方法中,也可以使用在构造方法中。
⭐️this出现在实例方法中其实代表的是当前对象。
⭐️this不能使用在静态方法中。
⭐️this. 大部分情况下可以省略,但是用来区分局部变量和实例变量的时候不能省略。
⭐️this() 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用。
🥅小试牛刀
❤️例1
(1)定义丈夫类 Husband 和妻子类 Wife,
丈夫类的属性包括:身份证号,姓名,出生日期,妻子。
妻子类的属性包括:身份证号,姓名,出生日期,丈夫。
(2)分别给这两个类提供构造方法(无参数构造方法和有参数构造方法都要提供),
编写测试程序,创建丈夫对象,然后再创建妻子对象,丈夫对象关联妻子对象,妻子对象关联丈夫对象,
要求能够输出这个“丈夫对象”的妻子的名字,或者能够输出这个“妻子对象”的丈夫的名字。并且要求在程序中演示出空指针异常的效果。
public class Test{
public static void main(String[] args){
//创建丈夫对象---调用有参构造方法
Husband h = new Husband("123456","张三","1999-1-14",null);
/*//第二种---------调用另一种有参构造方法也行
Husband h = new Husband("123456","张三","1999-1-14");
*/
//创建妻子对象--调用有参构造方法
Wife w = new Wife("456789","李四","2000-6-04",null);//这里null也可以换成h,在这里就把妻子和丈夫关联上
//让两者产生关联
h.wife = w;
w.husband = h;
//打印
System.out.println(h.name+"妻子是"+h.wife.name);
System.out.println(w.name+"丈夫是"+w.husband.name);
}
}
//---------------创建丈夫类
class Husband{
String idCard;
String name;
String birth;
Wife wife;
//无参构造方法
public Husband(){
}
//第一种----------有参构造方法
public Husband(String s1,String s2,String s3,Wife w){
idCard = s1;
name = s2;
birth = s3;
wife = w;
}
/*
//第二种----------有参构造方法
public Husband(String s1,String s2,String s3){//最后一个不写,默认赋值上null
idCard = s1;
name = s2;
birth = s3;
}
*/
}
//---------------创建妻子类
class Wife{
String idCard;
String name;
String birth;
Husband husband;
//无参构造方法
public Wife(){
}
//有参构造方法
public Wife(String s1,String s2,String s3,Husband h){
idCard = s1;
name = s2;
birth = s3;
husband = h;
}
}
❤️例2
请通过代码封装,实现如下需求:
编写一个类Book,代表教材:
1.具有属性:名称(title)、页数(pageNum)
2.其中页数不能少于200页,否则输出错误信息,并赋予默认值200
3.为各属性提供赋值和取值方法
4.具有方法:detail,用来在控制台输出每本教材的名称和页数
5.编写测试类BookTest进行测试:为Book对象的属性赋予初始值,并调用Book对象的detail方法,看看输出是否正确
public class Test01{
public static void main(String[] args){
//创建对象
Book b = new Book("高等数学",180);//页数不够200页
b.detail();//打印测试
Book b1 = new Book("大学物理",250);//页数够200页
b1.detail();//打印测试
//更改数据
b1.setPageNum(100);//页数不够200页
b1.detail();//打印测试
b1.setPageNum(300);//页数够200页
b1.detail();//打印测试
//打印结果
/*
页数不够200页,不够200页,默认赋值为200页
教材名称:高等数学, 教材页数:200
教材名称:大学物理, 教材页数:250
页数不够200页,默认赋值为200页
教材名称:大学物理, 教材页数:200
教材名称:大学物理, 教材页数:300
*/
}
}
//Book类
class Book{
//1. 变量私有化
private String title;
private int pageNum;
//2. 构造方法
public Book(){// 无法构造方法
}
public Book(String s,int i){// 有参构造方法
title = s;
if(i<200){
System.out.println("页数不够200页,不够200页,默认赋值为200页");
pageNum = 200;
}else{
pageNum = i;
}
}
//3. set和get接口
public void setTitle(String s){
title = s;
}
public String getTitle(){
return title;
}
public void setPageNum(int i){
if(i<200){
System.out.println("页数不够200页,默认赋值为200页");
pageNum = 200;
return;
}
pageNum = i;
}
public int getPageNum(){
return pageNum;
}
//4. 测试接口detail
public void detail(){
System.out.println("教材名称:"+title+", 教材页数:"+pageNum);//这里实际省略了this.
}
}
❤️例3
写一个名为Account的类模拟账户。该类的属性和方法如下所示:
(1)该类包括的属性:账户id,余额balance,年利率annualInterestRate;
(2)包含的方法:各属性的set和get方法。取款方法withdraw(),存款方法deposit()写一个测试程序
(1)创建一个Customer,名字叫Jane Smith,他有一个账号为1000,余额为2000,年利率为1.23%的账户
(2)对Jane Smith操作:
存入100元,再取出960元,再取出2000。
打印Jane Smith的基本信息
信息如下显示:
成功存入:100
成功取出:960
余额不足,取钱失败
public class Homework2{
public static void main(String[] args){
// 先创建一个账户对象Account
Account a = new Account("1000", 2000, 1.23);
// 创建客户对象
// 传给构造方法a是让Customer对象和Account对象产生关系。
Customer c = new Customer("Jane Smith", a);
/*进行测试操作
对Jane Smith操作:
存入100元,
再取出960元,
再取出2000。
*/
c.getAct().deposit(100);
c.getAct().withdraw(960);
c.getAct().withdraw(2000);
}
}
// 以后都是封装,所有的类都是属性私有化,对外提供setter and getter方法。
//1. 创建客户类Customer
class Customer{
// 客户名字
private String name;
// 客户手里应该有银行账户
private Account act;
//2. 构造方法
public Customer(){
}
public Customer(String name, Account act){
this.name = name;
this.act = act;
}
//3. setter and getter方法
// 为什么要写set和get,用得着吗?用不着你也得写,因为这是“封装”规定的。
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAct(Account act){
this.act = act;
}
public Account getAct(){
return act;
}
}
//1. 账户类Account
class Account{
private String id;
private double balance;
private double annualInterestRate;
//2. 构造方法
public Account(){// 无参构造
}
public Account(String id, double balance, double annualInterestRate){ // 有参构造
// 创建对象时需要的代码。
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
//3. setter and getter
public void setId(String id){
// 对象创建完之后,想修改id属性调用此方法。
this.id = id;
}
public String getId(){
return id;
}
// 实例方法(需要“引用.”来调用。)
public void setBalance(double balance){ //修改余额的方法。
this.balance = balance;
}
public double getBalance(){
return balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public double getAnnualInterestRate(){
return annualInterestRate;
}
//4. 存款、取款方法
// 参数表示存多少钱。
public void deposit(double money){
// 第一种
this.balance += money;//this.可省
// 第二种:调用方法来进行修改余额
this.setBalance(this.getBalance() + money);//this.可省
System.out.println("成功存入:" + money);
}
// 调用取钱的方法时,应该传递过来一个参数,告诉该方法要取多少钱
public void withdraw(double money){
// this(从this指向的对象中取款)
if(money > this.getBalance()){
System.out.println("余额不足,取钱失败");
return;
}
// 程序能够执行到此处说明余额充足
this.setBalance(this.getBalance() - money);
System.out.println("成功取出:" + money);
}
}
❤️例4
public class Test03{
public static void main(String[] args){
//第一种方法
// 调用无参构造方法
Student s1 = new Student();
// 赋值
s1.setName("张三");
s1.setAge(20);
s1.setAddress("北京朝阳区");
s1.setZipcode("122222");
s1.setMobile("12235224214");
//打印
System.out.println("姓名:"+s1.getName() + "," + s1.getPostAddress());
//第二种方法
//调用有参构造方法
Student s2 = new Student("李四",18,"深圳宝安区","111111","456456456456465");
//打印
System.out.println("姓名:"+s2.getName() + "," + s2.getPostAddress());
}
}
//1. 创建Student类-----封装
class Student{
private String name;
private int age;
private String address;
private String zipcode;
private String mobile;
//2. 构造方法
public Student(){
}
public Student(String name, int age, String address, String zipcode, String mobile){
this.name = name;
this.age = age;
this.address = address;
this.zipcode = zipcode;
this.mobile = mobile;
}
//4. getPostAddress方法
public String getPostAddress(){
return "地址:"+this.getAddress() + ",邮编:"+this.getZipcode();
//或者 return "地址:" + this.address + ",邮编:" + this.zipcode;
}
//3. set和get接口
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setAddress(String address){
this.address = address;
}
public String getAddress(){
return address;
}
public void setZipcode(String zipcode){
this.zipcode = zipcode;
}
public String getZipcode(){
return zipcode;
}
public void setMobile(String mobile){
this.mobile = mobile;
}
public String getMobile(){
return mobile;
}
}
🏀总结
❤️例1
到目前为止一个类当中可以出现的:
类体{
实例变量;
实例方法;
静态变量;
静态方法;
构造方法;
静态代码块;
实例语句块;
方法(){
// 局部变量
int i = 100;
}
}
public class Review{ // 类
// 类加载机制中,是这样的:在程序执行之前,凡是需要加载的类全部加载到JVM当中。
// 先完成加载才会执行main方法。
static{
System.out.println("Review类加载时执行!");
}
// 入口
// 静态方法
public static void main(String[] args){
// 局部变量
int i = 100;
// 完成一个对象的一连串动作。
// 一个学生在教室先学习,学习完成之后去餐厅吃饭。
Student s1 = new Student();
// 先学习,所有调用学习这个实例方法。
s1.study();
Student s2 = new Student();
}
}
// 学生类
class Student{
static{
System.out.println("Student类加载时执行!");
}
// 学号
private int no; // 实例变量
// 姓名
private String name;
// 学生有静态变量吗?
// 类级别的属性
static String job = "学习";
{
System.out.println("实例语句块,构造方法执行一次,这里就执行一次!");
}
// 构造方法
public Student(){
// 假设调用无参数的构造方法,默认创建的学生学号是100,名字是zhangsan
this(100, "zhangsan"); // this() 在这里也使用了。
}
public Student(int no, String name){
this.no = no; // 这里使用了this
this.name = name;
}
// 封装
// setter and getter方法
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setNo(int no){
this.no = no;
}
public int getNo(){
return no;
}
// 提供两个实例方法
public void study(){
// 私有的是可以在本类中访问的。在其它类中必须使用set和get方法。
//System.out.println(this.name + "正在努力的学习!");
//System.out.println(name + "正在努力的学习!");
// 在实例方法中调用本类其它的实例方法。
System.out.println(this.getName() + "正在努力的学习!");
//System.out.println(getName() + "正在努力的学习!");
// 方法执行到此处表示学习完成了,去吃饭。
//this.eat();
// this.可以省略
// 编译器检测到eat()方法是实例方法,会自动在eat()方法前添加 this.
eat();
}
public void eat(){ // 实例方法
System.out.println(this.getName() + "在餐厅吃饭呢!!!");
// 调用静态m1()方法
// 静态方法使用“类名.”的方式访问
// Student.m1();
// 类名. 可以省略吗?可以。
// java编译器会自动在m1()方法之前添加“类名.”,因为检测到m1()方法是一个静态方法。
m1();
}
// 提供两个静态方法
public static void m1(){
System.out.println("Student's m1 method execute!");
// 调用m2()方法
//Student.m2();
m2();
}
public static void m2(){
System.out.println("Student's m2 method execute!");
System.out.println("工作性质:" + job);
// 编译器检测到job是一个静态变量,所以这里会自动在job前添加:Student.
//System.out.println("工作性质:" + Student.job);
}
}
❤️例2
/*
程序再怎么变化,万变不离其宗,有一个固定的规律:
所有的实例相关的都是先创建对象,通过“引用.”来访问。
所有的静态相关的都是直接采用“类名.”来访问。
大结论:
只要负责调用的方法a和被调用的方法b在同一个类当中:
this. 可以省略
类名. 可以省略
*/
public class Review02{
int i = 100;
static int j = 1000;
public void m1(){
// 访问其他类的静态方法
T.t1();
// 访问其他类的实例方法
T t = new T();
t.t2();
}
public void m2(){}
// 实例方法
public void x(){ // 这个方法是实例方法,执行这个方法的过程中,当前对象是存在的。对象调用这个方法
m1(); //可以的,默认会加上this.
m2();
m3();
m4();
System.out.println(i); // System.out.println(this.i);
System.out.println(j); // System.out.println(Review02.i);
}
public static void m3(){}
public static void m4(){}
// 问?你怎么分析这个程序?
/*
第一步:
main方法是静态的,JVM调用main方法的时候直接采用的是“类名.”的方式。
所以main方法中没有this。
第二步:
m1() 和 m2() 方法是实例方法,按照java语法规则来说,实例方法必须先
new对象,通过“引用.”的方式访问。
*/
public static void main(String[] args){
// 编译报错。
//m1(); //默认加上this.但是是static方法,不行
//m2();
m3(); // 编译器会自动识别m3()静态方法,结果是:Review02.m3();
m4(); // Review02.m4();
//System.out.println(i); // 报错
System.out.println(j); // 可以
// 想访问m1() m2()还有i,你在static方法中只能自己new
Review02 r = new Review02();
System.out.println(r.i);
r.m1();
r.m2();
// 局部变量,局部变量访问的时候是不需要“xxx.”的
int k = 10000;
System.out.println(k);
}
}
class T{
// 静态方法
public static void t1(){
}
//实例方法
public void t2(){
}
}