从0开始学Java(007)_关键字this、static与代码块

关键字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()方法执行。在编写测试时,可以使用静态块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值