关键字this
this
关键字可以用于描述以下三种结构:
- 当前类中的属性:
this.属性
- 当前类中的方法(普通方法、构造方法):
this.方法()
,this()
- 当前对象
this调用属性
class Book {
private String title;
private double price;
public Book(String t, double p) {
title = t;
price = p;
}
// setter和getter方法略
public String getInfo() {
return "书名:" + title + ",价格:" + price;
}
}
public class Demo {
public static void main(String[] args) {
Book book = new Book("Java开发", 66.6);
System.out.println(book.getInfo());
}
}
上述代码,构造方法的功能是为title和price属性进行初始化。但在代码上存在不足:构造方法的参数t和p不够直观,无法体现具体含义
。
构造方法的功能是进行类的属性初始化,参数名最好与属性名一致。因此将代码改成下述形式:
public Book(String title, double price) {
title = title;
price = price;
}
修改后,执行程序发现构造方法中的参数值并未传递到属性。这是由于java代码以{}
为界限,当属性名与参数名一致时,默认情况下,值会传递到最近的
{}中的变量
。为了明确要访问的变量是类的属性,应在变量名前加this
。
public Book(String title, double price) {
this.title = title;
this.price = price;
}
开发时,访问类中属性,建议通过this.属性
调用
this调用方法
this
指的是当前对象,一个类除了属性还有方法,因此可以利用this
调用方法。
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public void print() {
System.out.println("*********");
}
// setter和getter方法略
public String getInfo() {
this.print();
return "书名:" + title + ",价格:" + price;
}
}
在类中调用普通方法,不论是否添加this
都一样,但为了代码的严谨性,建议使用this
调用。
- this调用构造方法
多个构造方法间互相调用:this(参数1, 参数2…);
范例:Book类中有三个构造方法,要求不论调用哪个构造方法都会输出提示信息一个新的Book类对象产生
class Book {
private String title;
private double price;
public Book() {
System.out.println("一个新的Book类对象产生");
}
public Book(String title) {
System.out.println("一个新的Book类对象产生");
this.title = title;
}
public Book(String title, double price) {
System.out.println("一个新的Book类对象产生");
this.title = title;
this.price = price;
}
// setter和getter方法略
public String getInfo() {
return "书名:" + title + ",价格:" + price;
}
}
上述代码中存在重复,要消除重复代码:
class Book {
private String title;
private double price;
public Book() {
System.out.println("一个新的Book类对象产生");
}
public Book(String title) {
this(); // 调用本类中的无参构造
this.title = title;
}
public Book(String title, double price) {
this(title); // 调用本类中的单参构造方法
this.price = price;
}
// setter和getter方法略
public String getInfo() {
return "书名:" + title + ",价格:" + price;
}
}
上述代码中存在限制:
- 利用
this
调用构造方法的语句只能放在构造方法首行;- 普通方法无法调用构造方法;
- 构造方法互相调用时,一定要保留调用的出口,即必须有个构造方法没有调用其他构造方法。
class Book {
private String title;
private double price;
public Book() { // 报错,构造递归调用
this("Book",1.1);
System.out.println("一个新的Book类对象产生");
}
public Book(String title) {
this(); // 调用本类中的无参构造
this.title = title;
}
public Book(String title, double price) {
this(title); // 调用本类中的单参构造方法
this.price = price;
}
// setter和getter方法略
public String getInfo() {
return "书名:" + title + ",价格:" + price;
}
}
上述代码会出现构造方法递归调用
错误,说明构造方法互相调用时,必须至少有一个构造方法没有使用”this()”调用其它构造方法。
范例:定义一个雇员类(编号,姓名,工资,部门),类中有4个构造方法;
- 无参构造:编号为0,姓名“无名氏”,工资为0.0,部门为”未定”;
- 单参构造(传递编号):姓名“临时工”,工资为800.0,部门为”后勤部”;
- 双参构造(传递编号、姓名):工资为2000.0,部门为”技术部”;
- 四参构造
实现方式1:不使用this
class Emp {
private int empNo;
private String eName;
private double sal;
private String dept;
public Emp() {
this.empNo = 0;
this.eName = "无名氏";
this.sal = 0.0;
this.dept = "未定";
}
public Emp(int empNo) {
this.empNo = empNo;
this.eName = "临时工";
this.sal = 800.0;
this.dept = "后勤部";
}
public Emp(int empNo, String eName) {
this.empNo = empNo;
this.eName = eName;
this.sal = 2000.0;
this.dept = "技术部";
}
public Emp(int empNo, String eName, double sal, String dept) {
this.empNo = empNo;
this.eName = eName;
this.sal = sal;
this.dept = dept;
}
// setter和getter方法略
public String getInfo() {
return "雇员编号:" + this.empNo + "\n" +
"姓 名:" + this.eName + "\n" +
"工 资:" + this.sal + "\n" +
"部 门:" + this.dept;
}
}
实现方式2:使用this
class Emp {
private int empNo;
private String eName;
private double sal;
private String dept;
public Emp() {
this(0,"无名氏",0.0,"");
}
public Emp(int empNo) {
this(empNo,"临时工",800.0,"后勤部");
}
public Emp(int empNo, String eName) {
this(empNo,eName,2000.0,"技术部");
}
public Emp(int empNo, String eName, double sal, String dept) {
this.empNo = empNo;
this.eName = eName;
this.sal = sal;
this.dept = dept;
}
// setter和getter方法略
public String getInfo() {
return "雇员编号:" + this.empNo + "\n" +
"姓 名:" + this.eName + "\n" +
"工 资:" + this.sal + "\n" +
"部 门:" + this.dept;
}
}
通过构造方法互相调用解决了代码重复问题。
当前对象
当前对象指的是当前正在调用类中属性或方法的对象
class Book {
public void print() {
// 哪个对象调用了print(),this就与该对象指向同一块内存地址
// this就是当前调用方法的对象
System.out.println("this = " + this);
}
}
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
System.out.println("bkA = " + bkA);
// bkA = com.java.demo.Book@1540e19d
bkA.print();
// this = com.java.demo.Book@1540e19d
System.out.println( "===============");
Book bkB = new Book();
// bkB = com.java.demo.Book@677327b6
System.out.println("bkB = " + bkB);
// this = com.java.demo.Book@677327b6
bkB.print();
}
}
class A{
private B b;
public A(){ // 2.执行A类构造方法
// 3. 实例化B类对象b,调用B类构造方法
this.b = new B(this); //此时this是temp
this.b.get(); // 5. 通过b调用B类的get()
}
public void print(){ //8. 执行该方法
System.out.println("Hello World !");
}
}
class B{
private A a;
public B(A a){ // A a = temp
this.a = a; // 4. 执行B类构造方法
}
public void get(){ // 6. 执行该方法
this.a.print(); // 7. 调用A类的print()
}
}
public class Demo {
public static void main(String[] args) {
// 1.实例化A类对象,调用A类的无参构造方法
A temp = new A(); // Hello World !
}
}
关键字static
static
关键字可以用于定义属性和方法。
static 定义属性
问题引出:
class Book {
private String title;
private double price;
// 为操作方便,暂不封装
String pub = "清华大学出版社";
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
}
}
public class Demo {
public static void main(String[] args) {
Book bkA = new Book("Java开发", 79.5);
Book bkB = new Book("JSP", 45.6);
System.out.println(bkA.getInfo()); // 清华大学出版社
System.out.println(bkB.getInfo()); // 清华大学出版社
bkB.pub = "北京大学出版社";
System.out.println(bkA.getInfo()); // 清华大学出版社
System.out.println(bkB.getInfo()); // 北京大学出版社
}
}
对上述代码进行内存分析:
通过内存分析,发现每个对象各自拥有相同的属性值pub,如果有1000个对象,要修改所有对象的pub属性,就需要进行1000次修改。但所有对象的pub属性都是一样的,可以将其定义为一个共享的属性,即所有对象都指向同一块pub属性。通过关键字static
关键字可以定义共享属性。
class Book {
private String title;
private double price;
// 为操作方便,暂不封装
static String pub = "清华大学出版社";
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getInfo(){
return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
}
}
public class Demo {
public static void main(String[] args) {
Book bkA = new Book("Java开发", 79.5);
Book bkB = new Book("JSP", 45.6);
System.out.println(bkA.getInfo()); // 清华大学出版社
System.out.println(bkB.getInfo()); // 清华大学出版社
bkB.pub = "北京大学出版社";
System.out.println(bkA.getInfo()); // 北京大学出版社
System.out.println(bkB.getInfo()); // 北京大学出版社
}
}
使用static
定义属性后,只要有一个对象修改属性值,那么所有对象的该属性值都会改变,内存分析如下:
static
定义的属性与普通属性区别在于保存数据的内存区域不同。static
定义的是公共属性,任由某个对象直接修改属性值是不合理的,应该由所有对象的代表进行属性访问,即用类访问。因此static
定义的属性,可直接用类名调用:Book.pub = "北京大学出版社";
普通属性必须由实例化的对象调用,而在没有实例化对象的情况下,static属性依然可以被调用。
public class Demo {
public static void main(String[] args) {
// 没有实例化对象的情况下,输出pub的内容
System.out.println(Book.pub);
}
}
由结果可知:static
虽然定义在类中,但不受对象控制,是独立于类存在的。
- 何时使用static定义属性?
编写类时,
static
不是首选修饰符,当需要描述共享信息时,才使用static,方便集体修改,不用重复开辟内存空间。
static 定义方法
static
定义的方法可以直接被类名调用
class Book {
private String title;
private double price;
// 为操作方便,暂不封装
private static String pub = "清华大学出版社";
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public static void setPub(String p){
pub = p;
}
public String getInfo(){
return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
}
}
public class Demo {
public static void main(String[] args) {
// 没有实例化对象的情况下,调用方法
Book.setPub("北京大学出版社");
Book bkA = new Book("Java开发", 79.5);
System.out.println(bkA.getInfo()); // 北京大学出版社
}
}
- 观察下述现象:
static方法不能直接使用非static属性或方法,只能调用static属性或方法。
public static void setPub(String p){
pub = p;
title = "sss"; // 报错,无法引用非静态变量
getInfo(); // 报错,无法引用非静态方法
System.out.println(this); // 报错,无法引用非静态变量
}
普通方法可以使用static属性或方法
public String getInfo(){
setPub(""); // 不报错
return ",出版社:" + this.pub; // 不报错
}
出现上述现象的原因:
- 普通属性和方法必须在对象实例化后分配了堆内存空间,才可以使用;
- static定义的方法和属性,不受实例化对象控制,可在没有实例化对象情况下访问。
- 一个方法定义在主类中,并由主方法直接调用,该方法定义格式如下:
public static 返回值类型 方法名(参数类型 参数, 参数类型 参数,...) {
方法体;
[return [返回值] ;] // []中内容可写可不写
}
一个方法定义在类中,由对象直接调用,其语法格式如下:
public 返回值类型 方法名(参数类型 参数, 参数类型 参数,...) {
方法体;
[return [返回值] ;] // []中内容可写可不写
}
观察代码:
public class Demo {
public static void main(String[] args) {
fun();
}
public static void fun() {
System.out.println("Hello World !");
}
}
没有static定义的fun()必须通过对象调用,主方法要使用fun()必须实例化对象
public class Demo {
public static void main(String[] args) {
// 产生对象,再利用对象调用非static方法
new Demo().fun();
}
public void fun() {
System.out.println("Hello World !");
}
}
- 定义类中方法时,
static
不是首选修饰符,因为每个对象可以利用自己的属性实现方法的不同调用。
class Flag {
private boolean flag;
public Flag(boolean flag) {
this.flag = flag;
}
public void fun() {
if (this.flag) {
System.out.println("可以操作");
} else {
System.out.println("不可以操作");
}
}
}
public class Demo {
public static void main(String[] args) {
Flag fA = new Flag(true);
Flag fB = new Flag(false);
fA.fun();
fB.fun();
}
}
当一个类中没有属性,只有方法时,建议将所有方法定义为static方法。这样就不用每次调用时都需要有实例化对象。
class MyMath {
public static int add(int x, int y) {
return x + y;
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(MyMath.add(10, 20));
}
}
主方法
1.主方法的组成:
组成 | 作用 |
---|---|
public | 主方法是程序的开始,所以主方法必须是可见、public(公共的)的 |
static | 证明此方法可直接由类名调用 |
void | 主方法是程序的开始,因此不能回头,执行完为止,所以不能有返回值 |
main | 系统规定好的方法名,不能修改 |
String [] args | 指的是程序运行时传递的参数 |
范例:对主方法传入参数
public class Demo {
public static void main(String[] args) {
for (int x = 0; x < args.length ; x++) {
System.out.println(args[x]); // 未输出参数,为空
}
}
}
多个参数时,必须使用空格分割。cmd执行的是java Demo 1 3 4 6
输出为:1 3 4 6
如果参数本身带有空格,需用""
描述
cmd执行的是java Demo "Hello world" "Hello Java"
static应用案例
已知:
- 多个对象,都使用同一个static属性;
- static定义方法可以避免实例化对象调用方法的限制。
- 实现对实例化对象个数的统计
- 要求:每实例化一个对象,就输出"这是第x个实例化对象"
- 思路:每次实例化对象,就会调用构造方法,因此可在构造方法中增加一个统计数据的操作,每当新对象产生,该属性值就自增加一。
class Book {
private static int num = 0;
public Book() {
num++;
System.out.println("这是第" + num + "个实例化对象");
}
}
public class Demo {
public static void main(String[] args) {
new Book();
new Book();
new Book();
}
}
- 实现属性的自动设置
要求:类中有一个无参构造方法,一个有参构造方法,有参构造方法的功能是传递title值。不论调用的哪个构造方法,均可为title赋值,且属性值尽量不重复。
class Book {
private String title;
private static int num = 0;
public Book() {
this("Title:No." + num++);
}
public Book(String title){
this.title = title;
}
public String getTitle(){
return this.title;
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(new Book("Java开发").getTitle()); // Java开发
System.out.println(new Book().getTitle()); // Title:No.0
System.out.println(new Book().getTitle()); // Title:No.1
}
}
总结
- 类定义属性或方法首时选不是static属性或方法;
- static属性或方法可直接用类名调用;
- static属性保存在全局数据区。
- 内存区有四种: 栈内存(对象的地址),堆内存(普通属性),全局数据区(static属性),全局代码区(所有的方法)
代码块
代码块是
{}
定义的一段语句,根据定义的位置和关键字的不同,代码块分为四种:普通代码块,构造块,静态块和同步代码块。
普通代码块
写在方法中的代码块,称为普通代码块。
public class Demo {
public static void main(String[] args) {
{ // 普通代码块
int num = 10; // 局部变量
System.out.println("num = " + num); // 10
}
int num = 100; // 全局变量
System.out.println("num = " + num); // 100
}
}
普通代码块的功能是防止方法中代码过多时出现变量重名问题,对方法的代码进行局部分割。**编写方法时,代码不应太长,**因此也没必要使用普通代码块。
构造块
写在类中的代码块,称为构造块。
class Book {
public Book(){ // 构造方法
System.out.println("A. Book类的构造方法");
}
{ // 构造块
System.out.println("B. Book类的构造块");
}
}
public class Demo {
public static void main(String[] args) {
new Book();
// B. Book类的构造块
// A. Book类的构造方法
}
}
输出结果显示:构造块的调用优先于构造方法。
静态块
static定义的代码块,称为静态块。静态块的使用,分为两种情况:
- 在非主类中使用
class Book {
public Book(){ // 构造方法
System.out.println("A. Book类的构造方法");
}
{ // 构造块
System.out.println("B. Book类的构造块");
}
static {
System.out.println("C. Book类的静态块");
}
}
public class Demo {
public static void main(String[] args) {
new Book(); // C B A
new Book(); // B A
}
}
结果显示:静态块优先于构造块。且不论实例化多少个对象,静态块只执行一次。static主要功能是为类中的static属性初始化。
class Book {
public static String msg;
static {
msg = "Hello".substring(0,2);
System.out.println("C. Book类的静态块");
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(Book.msg);
}
}
- 在主类中定义
public class Demo {
static {
System.out.println("**************");
}
public static void main(String[] args) {
System.out.println("Hello World !");
// **************
// Hello World !
}
}
结果显示:静态块优先于main()
方法执行。在编写测试时,可以使用静态块。