• JavaSE学习笔记(六)—— 面向对象


    一、面向对象思想

    1.1 面向对象思想引入

      前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。

    1.2 面向过程思想概述

      我们来回想一下,之前我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。
      在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。
      那么什么是面向过程开发呢? 面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
      面向过程的代表语言:C语言

    1.3 面向对象思想概述

      当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。接下来我们看看面向对象到底是什么?

      面向对象是基于面向过程的编程思想。

    面向过程:强调的是每一个功能的步骤
    面向对象:强调的是对象,然后由对象去调用功能

    1.4 面向对象的思想特点

      A:是一种更符合我们思想习惯的思想
      B:可以将复杂的事情简单化
      C:将我们从执行者变成了指挥者

    【举例】
      买电脑:
        面向过程:我的了解电脑--了解我自己的需求--找对应的参数信息--去中关村买电脑--讨价还价--买回电脑
        面向对象:我知道我要买电脑 -- 班长去给我买 -- 班长就买回来了
      洗衣服:
        面向过程:把衣服脱下--找一个盆--放点洗衣粉--加点水--把衣服扔进去--搓一搓--清洗衣服--拧干--晾起来
        面向对象:把衣服脱下--打开全自动洗衣机--扔进去--一键即可--晾起来
      吃饭:
        面向过程:去超市买菜--摘菜--洗菜--切菜--炒菜--盛起来--吃
        面向对象:上饭店吃饭,你--服务员(点菜)--厨师(做菜)--服务员(端菜)--吃

    1.5 面向过程和面向对象代码比较

      需求:把大象装进冰箱

    【面向过程】

      动作有哪些呢?

        A:打开冰箱门
        B:装进大象
        C:关闭冰箱门

      代码体现;

    class Demo {
        public static void main(String[] args) {
            /*
            System.out.println("打开冰箱门");
            //打开冰箱门的东西,我现在仅仅是为了演示,就写了一个输出语句
            //其实,它可能需要做很多操作。
            //这个时候代码就比较多一些了
            //假设我要多次打开冰箱门,
            //代码一多,每次都写一遍,麻烦不
            //我们就应该用方法改进
            
            System.out.println("装进大象");
            System.out.println("关闭冰箱门");
            */
            
            //写了方法以后,调用就改变了
            open();
            in();
            close();
        }
        
        public static void open() {
            System.out.println("打开冰箱门");
        }
        
        public static void in() {
            System.out.println("装进大象");
        }
        
        public static void close() {
            System.out.println("关闭冰箱门");
        }
    }

    【面向对象】

      我们怎么才能更符合面向对象思想呢?

        A:有哪些类呢?
        B:每个类有哪些东西呢?
        C:类与类直接的关系是什么呢?

      把大象装进冰箱的分析?

        A:有哪些类呢?

          大象
          冰箱
          Demo

        B:每个类有哪些东西呢?

          大象:
            进去
          冰箱:
            开门
            关门
          Demo:
          main方法
        C:类与类直接的关系是什么呢?
          Demo中使用大象和冰箱类的功能。

    class 大象 {
        public static void in() {
            System.out.println("装进大象");
        }
    }
    
    class 冰箱 {
        public static void open() {
            System.out.println("打开冰箱门");
        }
        
        public static void close() {
            System.out.println("关闭冰箱门");
        }
    }
    
    class Demo {
        public static void main(String[] args) {
            冰箱调用开门
            大象调用进去
            冰箱调用关门
        }
    }

    1.6 面向对象开发、设计、特征

    【面向对象开发】

      就是不断的创建对象,使用对象,指挥对象做事情。

    【面向对象设计】

      其实就是在管理和维护对象之间的关系。

    【面向对象特征】

      封装(encapsulation)
      继承(inheritance)
      多态(polymorphism)

    二、类与对象的关系

      我们学习编程语言,就是为了模拟现实世界的事物,实现信息化。比如:去超市买东西的计费系统,去银行办业务的系统。
      我们如何表示一个现实世界事物呢:
        属性:该事物的描述信息
        行为:该事物能够做什么
      举例:学生事物

        姓名,年龄,性别...

        学习,吃饭,睡觉

      我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。

      事物:       类:
        属性       成员变量
        行为          成员方法

      类:是一组相关的属性和行为的集合。是一个抽象的概念。

      对象:是该类事物的具体表现形式。具体存在的个体

      举例:
        学生:类
        班长:对象

      

    【类的定义】

      现实世界的事物:

    属性    人的身高,体重等
    行为    人可以学习,吃饭等

      Java中用class描述事物也是如此:

    成员变量    就是事物的属性
    成员方法    就是事物的行为

      定义类其实就是定义类的成员(成员变量和成员方法)

    【如何创建对象呢】

      格式:类名 对象名 = new 类名();

    【案例】

      定义一个学生类和学生测试类

    /*    
        学生事物:
            属性:姓名,年龄,地址...
            行为:学习,吃饭,睡觉...
            
        把事物要转换为对应的类:
        
        学生类:
            成员变量:姓名,年龄,地址...
            成员方法:学习,吃饭,睡觉...
        
        首先我们应该定义一个类,然后完成类的成员。
       如何使用成员变量呢?对象名.变量名
      如何使用成员方法呢?对象名.方法名(...)
    */ //这是我的学生类 class Student { //定义变量 //姓名 String name; //年龄 int age; //地址 String address; //定义方法 //学习的方法 public void study() { System.out.println("学生爱学习"); } //吃饭的方法 public void eat() { System.out.println("学习饿了,要吃饭"); } //睡觉的方法 public void sleep() { System.out.println("学习累了,要睡觉"); } } //这是学生测试类 class StudentDemo { public static void main(String[] args) { //类名 对象名 = new 类名(); Student s = new Student(); //输出成员变量值 //System.out.println(s.name); //System.out.println(s.age); //System.out.println(s.address); //改进写法 System.out.println(s.name+"---"+s.age+"---"+s.address); //给成员变量赋值 s.name = "林青霞"; s.age = 27; s.address = "北京"; //赋值后的输出 System.out.println(s.name+"---"+s.age+"---"+s.address); //调用方法 s.study(); s.eat(); s.sleep(); } }

      定义一个手机类

    /*
        手机事物:
            属性:品牌,价格,颜色...
            行为:打电话,发短信,玩游戏...
            
        手机类:
            成员变量:品牌,价格,颜色
            成员方法:打电话,发短信,玩游戏
    */
    class Phone {
        //品牌
        String brand;
        //价格
        int price;
        //颜色
        String color;
        
        //打电话的方法
        public void call(String name) {
            System.out.println("给"+name+"打电话");
        }
        
        //发短信的方法
        public void sendMessage() {
            System.out.println("群发短信");
        }
        
        //玩游戏的方法
        public void playGame() {
            System.out.println("玩游戏");
        }
    }

    三、对象内存图

    【一个对象的内存图】

    【二个对象的内存图】

    【三个对象的内存图】

    四、成员变量和局部变量的区别

    1. 在类中的位置不同
      成员变量:在类中方法外
      局部变量:在方法定义中或者方法声明上
    2. 在内存中的位置不同
      成员变量:在堆内存
      局部变量:在栈内存
    3. 生命周期不同
      成员变量:随着对象的创建而存在,随着对象的消失而消失
      局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
    4. 初始化值不同
      成员变量:有默认初始化值
      局部变量:没有默认初始化值,必须定义,赋值,然后才能使用。

    【注意事项】

       局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则

    class Varialbe {
        //成员变量
        //int num = 10;
        int num; //0
        
        public void show() {
            //int num2 = 20; //局部变量
            //可能尚未初始化变量num2
            //int num2; //没有默认值
            int num2 = 20;
            System.out.println(num2);
            
            //int num = 100;
            System.out.println(num);
        }
    }
    
    class VariableDemo {
        public static void main(String[] args) {
            Varialbe v = new Varialbe();
            
            System.out.println(v.num); //访问成员变量
            
            v.show();            
        }
    }

    五、形式参数问题

    • 基本类型:形式参数的改变不影响实际参数
    • 引用类型:形式参数的改变直接影响实际参数
    //形式参数是基本类型
    class Demo {
        public int sum(int a,int b) {
            return a + b;
        }
    }
    
    //形式参数是引用类型
    class Student {
        public void show() {
            System.out.println("我爱学习");
        }
    }
    
    class StudentDemo {
        //如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。
        public void method(Student s) { //调用的时候,把main方法中的s的地址传递到了这里 Student s = new Student();
            s.show();
        }
    }
    
    class ArgsTest {
        public static void main(String[] args) {
            //形式参数是基本类型的调用
            Demo d = new Demo();
            int result = d.sum(10,20);
            System.out.println("result:"+result);
            System.out.println("--------------");
            
            //形式参数是引用类型的调用
            //需求:我要调用StudentDemo类中的method()方法
            StudentDemo sd = new StudentDemo();
            //创建学生对象
            Student s = new Student();
            sd.method(s); //把s的地址给到了这里
        }
    }

    六、匿名对象

      匿名对象:就是没有名字的对象。

    【应用场景】

    1. 调用方法,仅仅只调用一次的时候。
      注意:调用多次的时候,不适合。因为每一次调用都会创建新的对象,浪费内存空间
    2. 匿名对象可以作为实际参数传递

    【好处】

      匿名对象调用完毕就是垃圾。可以被垃圾回收器回收。

    class Student {
        public void show() {
            System.out.println("我爱学习");
        }
    }
    
    class StudentDemo {
        public void method(Student s) {
            s.show();
        }
    }
    
    class NoNameDemo {
        public static void main(String[] args) {
            //带名字的调用
            Student s = new Student();
            s.show();
            s.show();//这里用的都是同一个对象
            System.out.println("--------------");
            
            //匿名对象
            //new Student();
            //匿名对象调用方法
            new Student().show();
            new Student().show(); //这里其实是重新创建了一个新的对象
            System.out.println("--------------");
            
            
            //匿名对象作为实际参数传递
            StudentDemo sd = new StudentDemo();
            //Student ss = new Student();
            //sd.method(ss); //这里的s是一个实际参数
            //匿名对象
            sd.method(new Student());
            
            //在来一个
            new StudentDemo().method(new Student());
         }
    }

    七、封装

      定义一个学生类:
        成员变量:name,age
        成员方法:show()方法

    class Student {
        //姓名
        String name;
        //年龄
        private int age;
        
        //写一个方法对数据进行校验
        /*
            返回值类型:void
            参数列表:int a
        */
        public void setAge(int a) {
            if(a < 0 || age > 120) {
                System.out.println("你给的年龄有问题");
            }else {
                age = a;
            }
        }
        
        //show()方法,显示所有成员变量值
        public void show() {
            System.out.println("姓名:"+name);
            System.out.println("年龄:"+age);
        }
    }
    
    class StudentDemo {
        public static void main(String[] args) {
            //创建学生对象
            Student s = new Student();
            s.show();
            System.out.println("--------------");
            
            //给成员变量赋值
            s.name = "林青霞";
            //s.age = 27;
            s.setAge(27);
            s.show();
            System.out.println("--------------");
            
            //给age赋值
            //s.age = -27; //这个数据是不合理的
            //通过方法给值
            s.setAge(-27);
            s.show();
            System.out.println("--------------");
        }
    }

      我们在使用这个案例的过程中,发现了一个问题:通过对象去给成员变量赋值,可以赋值一些非法的数据。这是不合理的。
      应该是这个样子的:在赋值之前,先对数据进行判断。判断到底在哪里做比较合适呢?StudentDemo类是一个测试类,测试类一般只创建对象,调用方法。 所以,这个判断应该定义在Student类中。
      而我们在成员变量的位置可不可以进行数据判断呢?是不可以的,因为做数据校验,必须要依靠一些逻辑语句。逻辑语句是应该定义在方法中的,所以,我们最终决定在Student类中提供一个方法来对数据进行校验。

      按照我们前面的分析,我们给出了一个方法进行校验。但是呢,它偏偏不调用方法来赋值,还是直接赋值了,这样我们的方法就没有起到作用。我就应该要求你必须使用我的方法,而不能直接调用成员变量赋值。怎么去强制要求不能直接使用成员变量呢?针对这种情况,Java就提供了一个关键字 private。

      其实我讲到现在讲解的是一个封装的思想。

    7.1 封装概述

      封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。   

    【好处】

    • 隐藏实现细节,提供公共的访问方式
    • 提高了代码的复用性
    • 提高安全性。

    【封装原则】

    • 将不需要对外提供的内容都隐藏起来。
    • 把属性隐藏,提供公共方法对其访问。

    7.2 private关键字

    • private一个权限修饰符
    • private可以修饰成员变量和成员方法
    • 被private修饰的成员只能在本类中被访问
    class Demo {
        //int num = 10;
        //用private修饰
        private int num = 10;
        
        public void show() {
            System.out.println(num);
        }
        
        private void method() {
            System.out.println("method");
        }
        
        public void function() {
            method();
        }
    }
    
    class PrivateDemo {
        public static void main(String[] args) {
            Demo d = new Demo();
            //不能方法私有的成员变量
            //System.out.println(d.num);
            d.show();
            //不能访问私有的成员方法
            //d.method();
            d.function();
        }
    }

    【封装和private的应用】

    • 把成员变量用private修饰
    • 提供对应的getXxx()和setXxx()方法
    //定义学生类
    class Student {
        //姓名
        private String name;
        //年龄
        private int age;
        
        //姓名获取值
        public String getName() {
            return name;
        }
        
        //姓名设置值
        public void setName(String n) {
            name = n;
        }
        
        //年龄获取值
        public int getAge() {
            return age;
        }
        
        //年龄赋值
        public void setAge(int a) {
            age = a;
        }
    }
    
    //测试类
    class StudentTest {
        public static void main(String[] args) {
            //创建学生对象
            Student s = new Student();
            
            //使用成员变量
            //错误:被私有修饰了,外界不能直接访问了
            //System.out.println(s.name+"---"+s.age);
            System.out.println(s.getName()+"---"+s.getAge());
            
            //给成员变量赋值
            //s.name = "林青霞";
            //s.age = 27;
            //通过方法给赋值
            s.setName("林青霞");
            s.setAge(27);
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }

    八、this关键字

      拿上面的Student类作为例子:

    //定义学生类
    class Student {
        //姓名
        private String name;
        //年龄
        private int age;
        
        //姓名获取值
        public String getName() {
            return name;
        }
        
        //姓名设置值
        public void setName(String n) {
            name = n;
        }
        
        //年龄获取值
        public int getAge() {
            return age;
        }
        
        //年龄赋值
        public void setAge(int a) {
            age = a;
        }
    }

      我们曾经说:起名字要做到见名知意。上面的setName(String n)和setAge(int a)显然不符合我们的命名规范,为了做到见明知意,应该定义为setName(String name)和setAge(int age),改进上面代码:

    //定义学生类
    class Student {
        //姓名
        private String name;
        //年龄
        private int age;
        
        //姓名获取值
        public String getName() {
            return name;
        }
        
        //姓名设置值
        public void setName(String name) { //name = "林青霞";
            name = name;
        }
        
        //年龄获取值
        public int getAge() {
            return age;
        }
        
        //年龄赋值
        public void setAge(int age) {
            age = age;
        }
    }
    
    //测试类
    class StudentTest {
        public static void main(String[] args) {
            //创建学生对象
            Student s = new Student();
            
            //给成员变量赋值
            s.setName("林青霞");
            s.setAge(27);
            //获取数据
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }

      当我们在测试类中给Student赋值时,却发现获取不到我们所期望的值。这时因为变量的使用规则:就近原则。调用s.setName("林青霞")赋值的时候,setName()方法中的name变量其实是该方法内部的局部变量,给它赋值并不会影响Student类中成员变量name的结果,此时getName()得到的结果仍然是默认初始化值null。

      那我们是否可以这样写:

       //姓名设置值
        public void setName(String name) { //name = "林青霞";
            //name = name; //变量的使用规则:就近原则
            Student.name = name;//错误的
        }

      这里是类名,目前还没有说过类似的用法,所以这个是有问题的。这里的调用只能通过对象名,这个对象如果存在,它应该代表的是Student的一个对象。那么,谁能够代表当前类的对象呢? java就提供了一个关键字 this。

      this:是当前类的对象引用。简单的记,它就代表当前类的一个对象。哪个对象调用那个方法,this就代表那个对象

      将上述代码改进:

    class Student {
        private String name;
        private int age;
        
        public String getName() {
            return name; //这里其实是隐含了this
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public int getAge() {
            return age;
        }
        
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    class StudentTest2 {
        public static void main(String[] args) {
            //创建一个对象
            Student s1 = new Student();
            s1.setName("林青霞");
            s1.setAge(27);
            System.out.println(s1.getName()+"---"+s1.getAge());
            
            //创建第二个对象
            Student s2 = new Student();
            s2.setName("刘意");
            s2.setAge(30);
            System.out.println(s2.getName()+"---"+s2.getAge());
        }
    }

    对应的内存图为:

    【this的应用场景】

      解决局部变量隐藏成员变量

    九、构造方法

      构造方法:给对象的数据进行初始化。

    【格式】

    • 方法名与类名相同
    • 没有返回值类型,连void都没有
    • 没有具体的返回值
    class Student {
        private String name; //null
        private int age; //0
        
        public Student() {
            System.out.println("这是构造方法");
        }
    }
    
    class ConstructDemo {
        public static void main(String[] args) {
            //创建对象
            Student s = new Student();
            System.out.println(s); //Student@e5bbd6
        }
    }

      我们一直在使用构造方法,但是,我们却没有定义构造方法,用的是哪里来的呢?

    【构造方法的注意事项】

    • 如果我们没有给出构造方法,系统将自动提供一个无参构造方法。(可以通过反编译工具看出)
    • 如果我们给出了构造方法,系统将不再提供默认的无参构造方法。注意:这个时候,如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法
    • 构造方法也是可以重载的
    class Student {
        private String name;
        private int age;
    
        public Student() {
            System.out.println("这是无参构造方法");
        }
        
        //构造方法的重载格式
        public Student(String name) {
            System.out.println("这是带一个String类型的构造方法");
            this.name = name;
        }
        
        public Student(int age) {
            System.out.println("这是带一个int类型的构造方法");
            this.age = age;
        }
        
        public Student(String name,int age) {
            System.out.println("这是一个带多个参数的构造方法");
            this.name = name;
            this.age = age;
        }
        
        public void show() {
            System.out.println(name+"---"+age);
        }
    }
    
    class ConstructDemo2 {
        public static void main(String[] args) {
            //创建对象
            Student s = new Student();
            s.show();
            System.out.println("-------------");
            
            //创建对象2
            Student s2 = new Student("林青霞");
            s2.show();
            System.out.println("-------------");
            
            //创建对象3
            Student s3 = new Student(27);
            s3.show();
            System.out.println("-------------");
            
            //创建对象4
            Student s4 = new Student("林青霞",27);
            s4.show();
        }
    }

    【注意】  

      A:给成员变量赋值有两种方式:setXxx()和构造方法,可以只写一种;

      B:如果不单独获取数据,可以不写getXxx()方法。

      学完构造方法后,以后再提类的组成:

    • 成员变量
    • 构造方法
    • 成员方法
      根据返回值:
          void类型
          非void类型
      形式参数:
          空参方法
          非空参方法

    十、类的初始化过程

      Student s = new Student();在内存中做了哪些事情?

    1. 加载Student.class文件进内存
    2. 在栈内存为s开辟空间
    3. 在堆内存为学生对象开辟空间
    4. 对学生对象的成员变量进行默认初始化
    5. 对学生对象的成员变量进行显示初始化
    6. 通过构造方法对学生对象的成员变量赋值
    7. 学生对象初始化完毕,把对象地址赋值给s变量

     

    十一、面向对象练习

      定义一个长方形类,定义 求周长和面积的方法,

    /*
        长方形的类:
            成员变量:
                长,宽
            成员方法:
                求周长:(长+宽)*2;
                求面积:长*宽
    
    */
    
    class ChangFangXing {
        //长方形的长
        private int length;
        //长方形的宽
        private int width;
        
        public ChangFangXing(){}
        
        //仅仅提供setXxx()即可
        public void setLength(int length) {
            this.length = length;
        }
        
        public void setWidth(int width) {
            this.width = width;
        }
        
        //求周长
        public int getZhouChang() {
            return (length + width) * 2;
        }
        
        //求面积
        public int getArea() {
            return length * width;
        }
    }
    
    class Test2 {
        public static void main(String[] args) {
            //创建键盘录入对象
            Scanner sc = new Scanner(System.in);
            
            System.out.println("请输入长方形的长:");
            int length = sc.nextInt();
            System.out.println("请输入长方形的宽:");
            int width = sc.nextInt();
            
            //创建对象
            ChangFangXing cfx = new ChangFangXing();
            //先给成员变量赋值
            cfx.setLength(length);
            cfx.setWidth(width);
            
            System.out.println("周长是:"+cfx.getZhouChang());
            System.out.println("面积是:"+cfx.getArea());
        }
    }

      定义一个员工类,自己分析出几个成员,然后给出成员变量,构造方法,getXxx()/setXxx()方法,以及一个显示所有成员信息的方法。并测试。

    /*
        分析:
            员工
                成员变量:
                    员工编号,姓名,年龄
                构造方法:
                    无参构造方法
                成员方法:
                    getXxx()/setXxx()
                    show();
    */
    class Employee {
        //员工编号
        private String employeeId;
        //姓名
        private String name;
        //年龄
        private int age;
        
        //构造方法
        public Employee() {}
        
        //getXxx()/setXxx()
        public String getEmployeeId() {
            return employeeId;
        }
        
        public void setEmployeeId(String employeeId) {
            this.employeeId = employeeId;
        }
        
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public int getAge() {
            return age;
        }
        
        public void setAge(int age) {
            this.age = age;
        }
        
        //显示所有成员信息的方法
        public void show() {
            System.out.println("员工编号是:"+employeeId+"的这个人是:"+name+"的年龄是:"+age);
        }
    }
    
    class EmployeeTest {
        public static void main(String[] args) {
            //创建对象
            Employee e = new Employee();
            
            //给成员变量赋值
            e.setEmployeeId("czbk9527");
            e.setName("唐伯虎");
            e.setAge(18);
            
            //获取数据
            //System.out.println(e.getEmployeeId()+"---"+e.getName()+"---"+e.getAge());
        
            //我们在Employee类中定义了一个show方法。所以,我们改进一下,使用show方法
            e.show();
        }
    }

    十二、static关键字

      定义一个人类

    class Person {
        //姓名
        String name;
        //年龄
        int age;
        //国籍
        //String country;
        static String country;
        
        public Person(){}
        
        public Person(String name,int age) {
            this.name = name;
            this.age = age;
        }
        
        public Person(String name,int age,String country) {
            this.name = name;
            this.age = age;
            this.country = country;
        }
        
        public void show() {
            System.out.println("姓名:"+name+",年龄:"+age+",国籍:"+country);
        }
    }
    
    class PersonDemo {
        public static void main(String[] args) {
            //创建对象1
            Person p1 = new Person("邓丽君",16,"中国");
            p1.show();
            
            //创建对象2
            //Person p2 = new Person("杨幂",22,"中国");
            //p2.show();
            Person p2 = new Person("杨幂",22);
            p2.show();
            
            //创建对象3
            //Person p3 = new Person("凤姐",20,"中国");
            //p3.show();
            Person p3 = new Person("凤姐",20);
            p3.show();
            
            p3.country = "美国";
            p3.show();
            
            p1.show();
            p2.show();
        }
    }

      姓名和年龄都是变化的,这个我能接收,因为每个人的姓名和年龄是不同的。但是,我们现在选取的几个人都是中国人,他们的国籍是一样的。一样的国籍,我每次创建对象,在堆内存都要开辟这样的空间,我就觉得有点浪费了。怎么办呢?

      针对多个对象有共同的这样的成员变量值的时候,Java就提高了一个关键字来修饰:static。

    【static的内存图解】——静态的内容存在于方法区的静态区

    【static的特点】——(它可以修饰成员变量,还可以修饰成员方法)

    1. 随着类的加载而加载
      回想main方法,main方法是static修饰的,所以它随着类的加载而加载。想象一下,如果main方法在class文件加载后它还没有加载,那么jvm调用的时候就找不到它了
    2. 优先于对象存在
    3. 被类的所有对象共享
      举例:咱们班级的学生应该共用同一个班级编号。
      其实这个特点也是在告诉我们什么时候使用静态?
          如果某个成员变量是被所有对象共享的,那么它就应该定义为静态的。
      举例:
          饮水机(用静态修饰)
          水杯(不能用静态修饰)
    4. 可以通过类名调用
      其实它本身也可以通过对象名调用。
      推荐使用类名调用。

      静态修饰的内容一般我们称其为:与类相关的,类成员

    【static关键字注意事项】

    1. 在静态方法中是没有this关键字的
      如何理解呢?
          静态是随着类的加载而加载,this是随着对象的创建而存在。
          静态比对象先存在。
    2. 静态方法只能访问静态的成员变量和静态的成员方法;非静态方法访问的成员变量,可以是静态的,也可以是非静态的,非静态方法访问的成员方法,可是是静态的成员方法,也可以是非静态的成员方法。简单记:静态只能访问静态
    class Teacher {
        public int num = 10;
        public static int num2 = 20;
        
        public void show() {
            System.out.println(num); //隐含的告诉你访问的是成员变量
            System.out.println(this.num); //明确的告诉你访问的是成员变量
            System.out.println(num2);
            
            //function();
            //function2();
        }
        
        public static void method() {
            //无法从静态上下文中引用非静态 变量 num
            //System.out.println(num);
            System.out.println(num2);
            
            //无法从静态上下文中引用非静态 方法 function()
            //function();
            function2();
        }
        
        public void function() {
        
        }
        
        public static void function2() {
        
        }
    }
    
    class TeacherDemo {
        public static void main(String[] args) {
            //创建对象
            Teacher t = new Teacher();
            t.show();
            System.out.println("------------");
            t.method();
        }
    }

    【静态变量和成员变量的区别】

    • 所属不同
      静态变量属于类,所以也称为为类变量
      成员变量属于对象,所以也称为实例变量(对象变量)
    • 内存中位置不同
      静态变量存储于方法区的静态区
      成员变量存储于堆内存
    • 内存出现时间不同
      静态变量随着类的加载而加载,随着类的消失而消失
      成员变量随着对象的创建而存在,随着对象的消失而消失
    • 调用不同
      静态变量可以通过类名调用,也可以通过对象调用
      成员变量只能通过对象名调用

    【main方法讲解】

      public static void main(String[] args) {...}

      public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。

      static:静态的,不需要创建对象,通过类名就可以。方便jvm的调用。
      void:因为我们曾经说过,方法的返回值是返回给调用者,而main方法是被jvm调用。你返回内容给jvm没有意义。
      main:是一个常见的方法入口。我见过的语言都是以main作为入口。
      String[] args:这是一个字符串数组。值去哪里了?
        这个东西到底有什么用啊?怎么给值啊?这个东西早期是为了接收键盘录入的数据的。格式是:java MainDemo hello world java

    十三、代码块

      在Java中,使用{}括起来的代码被称为代码块。根据其位置和声明的不同,可以分为

    • 局部代码块:局部位置,用于限定变量的生命周期。
    • 构造代码块:在类中的成员位置,用{}括起来的代码。每次调用构造方法执行前,都会先执行构造代码块。
      作用:可以把多个构造方法中的共同代码放到一起,对对象进行初始化。

    • 静态代码块:在类中的成员位置,用{}括起来的代码,只不过它用static修饰了。
      作用:一般是对类进行初始化。

      静态代码块,构造代码块,构造方法的执行顺序?静态代码块 -- 构造代码块 -- 构造方法

      注意:静态代码块只执行一次;构造代码块每次调用构造方法都执行

    class Code {
        //静态代码块
        static {
            int a = 1000;
            System.out.println(a);
        }
    
        //构造代码块
        {
            int x = 100;
            System.out.println(x);
        }
        
        //构造方法
        public Code(){
            System.out.println("code");
        }
        
        //构造方法
        public Code(int a){
            System.out.println("code");
        }
        
        //构造代码块
        {
            int y = 200;
            System.out.println(y);
        }
        
        //静态代码块
        static {
            int b = 2000;
            System.out.println(b);
        }
    }
    
    class CodeDemo {
        public static void main(String[] args) {
            //局部代码块
            {
                int x = 10;
                System.out.println(x);
            }
            //找不到符号
            //System.out.println(x);
            {
                int y = 20;
                System.out.println(y);
            }
            System.out.println("---------------");
            
            Code c = new Code();//1000  2000  100  200  code    
            System.out.println("---------------");
            Code c2 = new Code();//100  200  code
            System.out.println("---------------");
            Code c3 = new Code(1);//100  200  code
        }
    }

      写程序的执行结果。

    class Student {
        static {
            System.out.println("Student 静态代码块");
        }
        
        {
            System.out.println("Student 构造代码块");
        }
        
        public Student() {
            System.out.println("Student 构造方法");
        }
    }
    
    class StudentDemo {
        static {
            System.out.println("林青霞都60了,我很伤心");
        }
        
        public static void main(String[] args) {
            System.out.println("我是main方法");
            
            Student s1 = new Student();
            Student s2 = new Student();
        }
    }

      

  • 相关阅读:
    常用工具-notepad++打开大文件卡死现象
    ORM框架之EntityFramework
    System.Data.Entity 无法引用的问题
    js 数组、对象转json 以及json转 数组、对象
    js中(function(){xxx})();写法解析以及function与!感叹号
    js 里面call()的使用
    Javascript继承机制的设计思想
    JS中let和var的区别
    利用JS实现图片的缓存
    实例详述FOR XML PATH用法
  • 原文地址:https://www.cnblogs.com/yft-javaNotes/p/10806235.html
Copyright © 2020-2023  润新知