• 构造方法、This关键字、静态与封装的特性与作用


    1.构造方法

    构造方法是一种特殊的方法,专门用于构造/实例化对象。

    构造方法根据是否有参数分为无参构造方法和有参构造方法。

    1.1无参构造方法

    无参构造方法就是构造方法没有任何参数。无参构造方法在创建(new class类名())时调用;无参构造方法一般用于给属性赋默认值。语法:

    [修饰符]  类名(){
    
      ....... 
    
    }

    例如:

     1 public class Dog{  
     2     
     3     String name;
     4     int health;
     5     int love;
     6     String strain;
     7     
     8     public Dog(){    //无参构造方法
     9     System.out.println("构造方法"); 
    10 health = 100; 11 love = 0; 12  } 13 14 }

    如果开发中没有定义无参构造方法,jvm(虚拟机)默认给类分配一个无参构造方法。

    1.2有参构造方法

    当构造/实例化对象时,可以向方法中传递参数,这样子构造的方法叫有参构造方法。

    语法:

    [修饰符] 类名(Type arg1,Type arg2,…){
        // 初始化代码
    }

    注:有参构造方法与无参构造方法是方法重载。

    1.2.1有参构造常见的问题

    如果一个类中提供了有参构造的方法,则JVM(虚拟机)中就不再给该类分配无参构造。

    例如:

    public class Dog{
       String name;
        int health;
        int love;
        String strain;
        
        public Dog(String _name,int _health,int _love,String _strain){  //定义了一个有参构造方法但没有定义无参构造方法
            name = _name;
            health = _health;
            love = _love;
            strain = _strain;
        }
     1 public class TestCar{
     2     public static void main(String[] args){
     3         Dog dog = new Dog();   //调用无参构造方法
     4         dog.name = name;
     5         dog.health = 100;
     6         dog.love = 0;
     7         dog.strain = strain;
     8 
     9 
    10         }
    11 }                        

    运行结果出错:此时jvm不在默认分配无参构造方法

    注意:在开发过程中,如果开发者提供了有参构造方法,一定要习惯性的提供无参构造。

     2.This关键字

    对象初始化内存图

    对象初始化内存图中的方法区位于栈和堆内存的上方,用于保存class和各种方法,对象初始化的过程中最先把class加载该区域,读取class文件中定义的属性和方法,根据定义的属性计算申请内存需要的字节数。

    然后用 new在堆中申请内存并初始化内存。并在堆中生成This关键字,This中存放的是该对象本身的地址,也就说当调用This关键字时,还是指向对象本身。this可用于访问本对象属性,同时解决局部变量和成员变量同名的问题(成员变量可以用this.成员变量名代替)。

    例如

    1 public Dog2(String name,int health,int love,String strain){
    2 System.out.println("this:"+this);
    3     this.name = name;  //this.name代表的就是成员变量(成员属性),name代表的是你输入名。
    4     this.health = health;
    5     this.love = love;
    6     this.strain = strain;
    7 }

    通过打印this中的引用,可以看出对象dog和this指向同一内存。

    一般而言,dog用于类的外部,this用于类的内部。因为类的内部根本不知道 car 变量名的存在。

    方法的调用内存图

    因为在方法中调用了该对象的属性,我们可以用This指向该对象,可以更加快捷的找到其属性值。

    1 public void showInfo(){
    2         System.out.print("我的名字叫"+this.name); //this.属性名 访问
    3         System.out.print(",健康值"+this.health);
    4         System.out.print(",亲密度"+this.love);
    5         System.out.println(",我是一只"+this.strain); 
    6 }

    总结:

    [1] this调用属性

    [2] this调用方法

    [3] this调用本类的构造方法。形如:

     1 public Dog(String name,int health,int love){
     2         this.setName(name);       //调用方法
     3         this.setHealth(health);
     4         this.setLove(love);
     5     }
     6     
     7 
     8     public Dog(String name,int health,int love,String strain){
     9         //this.setName(name);
    10         //this.setHealth(health);
    11         //this.setLove(love);
    12         
    13         // this调用本类的其他构造方法
    14         this(name,health,love);  //调用本类的
    15         this.setStrain(strain);
    16         
    17         // showInfo();
    18         //this.showInfo();
    19     }

    3.静态(static)

    static 关键字表示静态,可以修饰变量,也可以修饰方法。一个类中包含静态成员(静态变量和静态方法)和实例成员(实例变量也叫属性和实例方法)

    下面有这样的一个简单的需求可用static实现

    需求:统计汽车工厂生成了多少量车?

    ð  统计工厂生成了多少量汽车的功能应该放到类功能上,不应该属于某个对象。

    ð  声明一个变量用于统计个数,这个变量应该被类的实例共享。(static)

    ð  被类的实例共享的区域在方法区(Car.class)

    ð  用static关键字声明这样的变量

     1 public class Car{
     2     String brand;
     3     String type;
     4     int price;
     5     static int count; //静态变量的使用方法
     6     public Car(){
     7         count = 0;
     8     }
     9     
    10     public Car(String brand,String type,int price){
    11         this.brand = brand;
    12         this.type = type;
    13         this.price = price;
    14         count++;
    15         showInfo();
    16     
    17     }
    18     
    19     public void showInfo(){
    20         System.out.println("车辆信息:");
    21         System.out.println("品牌:"+this.brand);
    22         System.out.println("型号:"+this.type);
    23         System.out.println("价格:"+this.price);
    24         System.out.println("我是第"+Car.count+"辆车"); //静态变量的访问方式
    25     
    26     
    27 
    28     }
    29 }
     1 public class TestCar{
     2     public static void main(String[] args){
     3         Car car1 = new Car("奔驰","漏油GL300",66);
     4         
     5         
     6         
     7         Car car2 = new Car("奔驰","漏油GL400",66);
     8         
     9         
    10     }
    11 }

    从运行效果图可以我们看出定义的静态变量count并属于哪一个对象,他是属于类所有的,类中的所有对象都可以访问。其区别于其它实例变量(brand、type、price)归实例(对象)所有,每个不同的对象都可以赋不同的属性值,各个对象是之间没有什么联系的。下面我们来详细认识什么是静态变量。

    3.1静态变量

    static 修饰的变量称为静态变量/静态属性,语法:

    static 类型 变量名称 [= 初始值]

    静态变量归类所有,也叫类变量,分配在方法区(共享区)中的静态区,可以被类的实例(对象)共享访问。

    内存图

    静态变量的访问方法为:

    [1] 类名.静态变量(推荐):更好的说明静态变量归类所有

    [2] 对象.静态变量

    3.2静态方法

    用static修饰的方法我们称为静态方法。使用的语法:

    [修饰符] static 返回值类型 方法名(arg…){
    }

    静态方法也归类所有,调用形式和访问静态变量一样。

    [1] 类名.方法名() (推荐)

    [2] 对象.方法名()

    静态方法在使用过程中遇到的常见问题:

    用静态方法访问实例变量

    public class Car{
        String brand;
        String type;
        int price;
        static int count;
        public Car(){
            count = 0;
        }
        
        public Car(String brand,String type,int price){
            this.brand = brand;
            this.type = type;
            this.price = price;
            count++;
            showInfo();
        
        }
        
        public void showInfo(){
            System.out.println("车辆信息:");
            System.out.println("品牌:"+this.brand);
            System.out.println("型号:"+this.type);
            System.out.println("价格:"+this.price);
            System.out.println("我是第"+Car.count+"辆车");
        }
        public static int getCarCount(){
            //定义一个静态方法访问实例变量
            System.out.println("价格:"+this.price);
        }
        
    }

    运行结果:

    用静态方法访问实例方法

    public static int getCarCount(){
            //定义一个静态方法访问实例变量
            //System.out.println("价格:"+this.price);
            //静态方法访问实例方法
            this.showInfo();
        }

    运行结果:

    总结:可以看出通过静态方法并不能访问实例成员(实例变量与实例方法);但是实例方法却可以访问静态成员(静态变量、静态方法);

    通过静态方法可以访问静态变量

    public static int getCarCount(){
            //定义一个静态方法访问实例变量
            //System.out.println("价格:"+this.price);
            //静态方法访问实例方法
            //this.showInfo();
            //返回一个静态变量count
            return Car.count;
            
        }
    public class TestCar{
        public static void main(String[] args){
            Car car1 = new Car("奔驰","漏油GL300",66);
            
            
            
            Car car2 = new Car("奔驰","漏油GL400",66);
            System.out.println(Car.getCarCount());   //调用静态方法并把返回值打印出来
            
            
        }
    }

    运行结果:

    3.3静态常量

    在程序运行过程中,如果一个量的值不会发生改变,可以把该量声明为静态常量,用static final修饰。

     1 public class Penguin{
     2     
     3     private String name;
     4     private int health;
     5     private int love;
     6     private String gender;
     7     
     8     static final String SEX_MALE = "雄";
     9     static final String SEX_FEMALE = "雌";
    10 
    11 ....
    12 }

    作用:方便日后修改代码,减少重复操作。

    4.类的加载机制

    为什么:[1]实例方法可以访问静态成员。

        [2]静态方法不能访问非静态成员。

    当实例化一个对象时(Car car  = new Car(…);)jvm首先把Car.class加载到方法区,然后

    [1]读取Car.class 根据声明的成员变量计算申请内存需要的字节数

    [2]读取Car.class 中的静态成员,给静态变量在方法区分配空间并初始化。

    接着new Car 申请内存得到一个car对象,此时才有对象的空间。showInfo才可以通过car对象调用。

    也就是说程序运行时是先给静态变量分配空间初始化,那时的对象还没在堆中分配有空间(通俗来说还没产生对象),这时怎么可能调用呢!

    5.小结

    6.封装

    封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

    封装的步骤:

    [1]属性私有化

    [2]提供公共的设置器和访问器

    [3]在设置器和访问器中添加业务校验逻辑

     1 public class Penguin{
     2     String name;
     3     // 【1】private 私有的,对外不可见
     4     private int health;
     5     int love;
     6     String gender;
     7      // 【2】提供公共的设置器(setter)和访问器(getter)
     8     public void setHealth(int health){
     9          // 【3】逻辑校验
    10         if(health <0){
    11             this.health = 60;
    12             System.out.println("健康不合法");
    13         }else{
    14             this.health = health;
    15         }
    16     }
    17     public int getHealth(){
    18         return this.health;
    19     }
    20     
    21     public Penguin(){
    22         
    23     }
    24     public Penguin(String name,String gender){
    25         this.name = name;
    26         this.gender = gender;
    27     }
    28     public Penguin(String name,String gender,int health,int love){
    29         this(name,gender);
    30         this.setHealth(health);
    31         this.love = love;
    32     }
    33     
    34     public void showInfo(){
    35         System.out.print("我的名字叫"+this.name);
    36         System.out.print(",健康值"+this.health);
    37         System.out.print(",和主人的亲密度"+this.love);
    38         System.out.println(",性别"+this.gender);
    39         
    40     }
    41 }
     1 public class Test01{
     2     public static void main(String[] args){
     3         //new 得到构造方法(调用构造方法)
     4        // Penguin penguin = new Penguin("美美","Q妹");
     5         //penguin.showInfo();
     6         Penguin penguin02 = new Penguin("壮壮","Q仔",-10,10);
     7         penguin02.showInfo();
     8         //Penguin penguin03 = new Penguin();
     9         //penguin03.setHealth(-10);
    10         //penguin03.showInfo();
    11     }
    12 }

    运行结果:

    从打印结果可以看出,当你输入的健康值小于0时,经过你设定的逻辑校验时,会重新分配默认60。

  • 相关阅读:
    操作标签(转载)
    创建标签(转载)
    标签管理(转载)
    mysql第四篇--SQL逻辑查询语句执行顺序
    mysql第四篇:数据操作
    mysql第四篇:数据操作之单表查询
    mysql第三篇:表操作
    MySQL系列
    Mysql 第二篇:库操作
    Mysql 第一篇:初识数据库
  • 原文地址:https://www.cnblogs.com/ruckly/p/10743432.html
Copyright © 2020-2023  润新知