今天我们主要学习了如下内容:
1.类和对象的基本概念,类定义的语法
2. 创建对象的语法访问对象成员变量和成员方法的语法
3. 从数据类型的角度理解类和对象,以及对象的内存图解。
4.面向对象特殊语法:
a. 成员变量 VS局部变量
b. 方法形参类型为引用类型时的特殊例子。
c. 构造方法
d. this关键字
1. 面向对象引入
回想一下客观世界,我们的客观世界是由两种东西所组成的:
- 生活在客观世界中的个体(客体,或物体)
- 以及个体之间的联系
正是由于现实世界中的个体们,“各司其职”, 直接或间接的“相互协作”,才保证了这个世界的正常,有序的流转。
我们面向对象的程序世界和现实世界也极其相似:
- 运行中的程序,由多个个体(也就是对象)组成
- 运行中的个体,需要相互协作,共同完成程序的功能
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 面向对象的理解: 8 * 1. 类比于现实世界中的个体(客体),运行中的面向对象程序也是由一个一个的 对象 (个体)组成 9 * 2. 所以,要想让面向对象程序运行,必须首先学,构造对象,对象中包含哪些内容呢? 10 * 属性: 11 * 行为: 12 * 3. 也就是说,要想创建一个对象,就必须向jvm描述,对象的 属性和行为 ,但是我们不可能对每一个对象 13 * 都去向jvm描述一次,该对象的属性和行为(因为我们的程序中,可能会有成千上万个对象) 14 * 15 * 4. 如何向jvm描述,我们要创建怎么样的对象呢? 核心从对象的 属性和行为 16 * 将同种类型的对象的属性和行为共性,抽取出来 ——> 类(描述对象),描述同种类型的所有对象所具有的属性和行为 17 * 18 * 类 和 对象 19 * 20 * 5. 类和对象,以及对象与对象之间的关系 21 * 类 VS 对象: 22 * 类: 描述,同种类型的对象,共有的属性和行为,但是类描述的是对象有哪些属性,有哪些行为 23 * 对象: 对象属性的具体取值,类并没有规定,也就是说,对象属性的具体取值,是由每一个具体的对象自己来决定的 24 * 25 * 对象 VS 对象: 26 * a: 不同类型的对象,对象可能具有不同的属性和行为 27 * b:同种类型的对象,也有区别,区别就在同种类型的不同对象,他们的属性取值,可以不同 28 */ 29 public class Introduction { 30 31 }
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 我们要向让写好的java程序运行起来, 8 * 得有对象,所以首相应该学习如何创建对象,但是,要创建对象,必须首先,描述对象 -> 类来描述对象 9 * 10 * 所以,为了创建出对象,学习类定义,来描述同种类型的所有对象(属性和行为) 11 * 12 * 定义一个类,类比于现实世界,说明一种类型(属性和行为) 13 * 14 * 现实中的事物类型(比如人这个类型): 15 属性 人的身高,体重.... 16 行为 人可以,吃饭,睡觉, 学习,思考…… 17 18 Java语言中定义类,也是从这两个方面入手 19 成员变量 就是事物的属性 20 成员方法 就是事物的行为 21 22 成员变量(对比局部变量): 成员变量和局部变量定义的位置不同,成员变量定义在,方法体之外 23 成员方法(对比我们之前的方法): 之前的方法 修饰符都是 public static,修饰符中去掉static修饰符的方法就是成员方法 24 25 Java中定义类其实就是定义类的成员(成员变量(属性)和成员方法(行为)) 26 27 */ 28 public class Demo1 { 29 // 成员变量 30 int a; 31 32 33 // 成员方法 34 public void memberMethod() { 35 36 } 37 38 39 40 }
2. 面向对象基本概念
也就是说,要想写出面向对象的程序,必须首先学会构建面向对象程序的基本组成单位——对象。如何描述一个对象呢?试想下如下场景:
假设你面对一个对地球一无所知的X星人,你要向他描述一只他从未见过的小花猫,你会怎么向他描述呢?
- 属性(外貌)
- 行为
那世界上有千千万万只小花猫,如果都要让你描述,你怎么办呢?你显然是无法具体一一描述的。但是这千千万万只小花猫,在属性和外貌上很多的共性 可以把这些共性抽取出来
从一般个体中抽取出来的共性,即对某种类型个体的一般性描述(属性和行为),其实就是类。
类:同种物体在属性和行为上的集合与抽象。
类和对象的关系
- 类描述出了,该种类型对象共有的属性和行为
- 类描述了,对象有哪些属性,具备哪些行为(包括行为的具体实现)。
- 但是各个对象的属性取什么值,只有具体的对象能确定。
比如,对于小花猫这个类而言,我们知道 小花猫颜色是花的 小花猫的颜色到底有多花,哪部分花,每个具体的小花猫对象各不相同
3. 面向对象基本语法
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 定义一个学生类: 8 * 属性(成员变量): 姓名, 性别, 年龄,学号 9 * 行为(成员方法): 吃饭, 睡觉,学习 10 * 11 * 有了类定义,我们就有了对同种类型的对象的描述,我们就可以创建学生对象了 12 * a. 创建对象 13 * 类名 对象名 = new 类名(); 14 * b. 给对象的属性赋值, 或者访问对象的属性 15 * 对象名.属性名 16 * c. 访问对象的行为 17 * 对象名.方法 18 * 19 * 类名 对象名 = new 类名(); 20 对象名.成员变量; 21 对象名.成员方法 22 23 */ 24 public class Demo2 { 25 26 public static void main(String[] args) { 27 // 类名 对象名 = new 类名(); 28 Student1 stu1 = new Student1(); 29 30 //每个对象,的属性都可以有不同取值 31 stu1.name = "zhangsan"; 32 stu1.age = 20; 33 stu1.isMale = true; 34 stu1.sno = 1; 35 System.out.println(stu1.name + "--" +stu1.age + "--" + stu1.isMale + "--" + stu1.sno) ; 36 37 //访问对象行为 38 stu1.eat(); 39 40 41 } 42 43 } 44 45 /* 46 定义学生类,来描述所有学生对象,共有的属性和行为 47 */ 48 class Student1 { 49 50 // 姓名 51 String name; 52 53 //性别 54 boolean isMale; 55 56 //年龄 57 int age; 58 59 //学号 60 int sno; 61 62 // 学生具有的吃饭行为 63 public void eat() { 64 System.out.println(name + "吃饭"); 65 } 66 67 68 //睡觉行为 69 public void sleep() { 70 System.out.println("sleeping"); 71 } 72 73 //学习 74 public void study() { 75 System.out.println(sno + " 在学习"); 76 } 77 78 }
从语法层面再次认识类和对象:
- 回忆一下在基础语法部分我讲过的什么是数据类型?
- 再回忆一下,Java语言中的类的组成,你是否能得到什么启示呢?
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 从语法层面再次认识类和对象: 7 8 1. 回忆一下在基础语法部分我讲过的什么是 数据类型? 9 数据类型:一个数据集合和基于这个数据集合的一组操作 byte short int char 10 11 2. 类定义:类体中包括,成员变量和成员方法 12 类定义中的数据集合: 成员变量的集合 13 类中定义的操作集合: 成员方法集合 14 15 所有说白了,一个类定义,其实就是一种数据类型的定义,这种数据类型,比之于byte,short,int,char, 16 不一样,因为,我们自己定义的类 -> 其实是coder的自定义数据类型,所以jvm天生不认识 17 18 那么,一旦我们将类看做是一种自定义数据类型,类和对象的关系,就可以类比于基本数据类型 和 基本数据类型变量之间的关系 19 int a; 20 Student1 st = new Student1(); 21 22 * 23 */ 24 public class Demo3 { 25 26 }
4. 内存中的“对象”
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 从内存的角度,理解对象: 8 * 9 * 1个对象的内存图 10 一个对象的基本初始化过程 11 12 3个对象的内存图 13 其中有两个引用指向同一个对象 14 15 */ 16 public class Demo4 { 17 18 public static void main(String[] args) { 19 20 // 类名 对象名 = new 类名(); 21 Student2 stu1 = new Student2(); 22 //每个对象,的属性都可以有不同取值 23 stu1.name = "张三"; 24 stu1.age = 20; 25 stu1.isMale = true; 26 stu1.sno = 1; 27 //System.out.println(stu1.name + "--" +stu1.age + "--" + stu1.isMale + "--" + stu1.sno) ; 28 29 Student2 stu2 = new Student2(); 30 //每个对象,的属性都可以有不同取值 31 stu2.name = "李四"; 32 stu2.age = 20; 33 stu2.isMale = true; 34 stu2.sno = 1; 35 //System.out.println(stu1.name + "--" +stu1.age + "--" + stu1.isMale + "--" + stu1.sno) ; 36 37 //访问对象行为 38 //stu1.eat(); 39 //stu2.eat(); 40 41 42 // stu1 和 stu3, 指向了同一个对象 43 Student2 stu3 = stu1; 44 45 stu3.name = "王五"; 46 47 System.out.println(stu1.name); 48 49 } 50 51 } 52 53 /* 54 定义学生类,来描述所有学生对象,共有的属性和行为 55 */ 56 class Student2 { 57 58 // 姓名 59 String name; 60 61 //性别 62 boolean isMale; 63 64 //年龄 65 int age; 66 67 //学号 68 int sno; 69 70 // 学生具有的吃饭行为 71 public void eat() { 72 System.out.println(name + "吃饭"); 73 } 74 75 76 //睡觉行为 77 public void sleep() { 78 System.out.println("sleeping"); 79 } 80 81 //学习 82 public void study() { 83 System.out.println(sno + " 在学习"); 84 } 85 86 }
1个对象的内存图
一个对象的基本初始化过程
图:一个对象的内存映像
3个对象的内存图
其中有两个引用指向同一个对象
图:多个对象的内存映像
5. 面向对象“特殊”语法
在学习类(Class)的时候,我们引入了一种和之前不太一样的变量——成员变量。其实,之前我们在代码中所使用的所有的变量都是局部变量。
哪些变量是局部变量? 方法中定义的变量和方法的形式参数
1 package com.cskaoyan.syntax.variable; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 成员变量: 定义在类体中,方法体之外的变量 8 * 哪些变量是局部变量? 方法中定义的变量和方法的形式参数 9 10 局部变量和成员变量的比较: 11 1. 在类中定义的位置不同 12 13 2. 在内存中的位置不同 14 a. 局部变量,都是存储在方法对应的栈帧中的 15 b. 成员变量,存储在堆上,在对象的内存中 16 3. 初始化值不同 17 a. 局部变量,局部变量没有天生的初始值,必须在使用之前,用代码初始化成员变量的值 18 b. 成员变量,成员变量存储在堆上的,堆上的成员变量,天然有默认初值 19 20 4.生命周期不同 21 a. 因为局部变量是存储在栈帧中的,因此,随着栈帧的存在而存在,随着栈帧的销毁而销毁 22 b. 成员变量,因为成员变量,是在堆上,对象的内存空间中, 23 成员变量随着对象的存在而存在,随着对象的销毁而销毁(当对象无法被使用的时候,也就是说对象变成垃圾的时候) 24 25 */ 26 public class Demo1 { 27 28 int intValue; 29 30 31 32 public void test() { 33 int i = 10; 34 System.out.println(i); 35 36 System.out.println(intValue); 37 } 38 39 }
在进入面向对象之后,大家应该也已经清楚了,引用变量的含义。 我们知道,在方法定义中的形式参数的类型,既可以是基本数据类型,也可以是引用变量类型。 那么,当方法的参数类型是引用类型的时候,在方法的形参与实参进行值传递的时候,就会引起新的问题,接下来我们做一个练习。
这两段代码实现的功能有什么不同呢,为什么?
代码1
1 package com.cskaoyan.syntax.method.excercise; 2 3 /** 4 * @version 1.0 5 */ 6 7 class Demo1 { 8 int a; 9 } 10 11 public class TestQuote1 { 12 13 public static void main(String args[]) { 14 Demo1 d1 = new Demo1(); 15 d1.a = 1; 16 17 Demo1 d2 = new Demo1(); 18 d2.a = 2; 19 20 System.out.println(d1.a); 21 System.out.println(d2.a); 22 function(d1, d2); 23 System.out.println(d1.a); 24 System.out.println(d2.a); 25 } 26 27 // 方法的参数类型,都是引用类型 28 private static void function(Demo1 d1, Demo1 d2) { 29 //交换 d1 和 d2这两个引用变量所指向的对象的 成员变量a的值 30 int a; 31 a = d1.a; 32 d1.a = d2.a; 33 d2.a = a; 34 } 35 }
代码2
1 package com.cskaoyan.syntax.method.excercise; 2 3 /** 4 * @version 1.0 5 */ 6 class Demo2 { 7 int a; 8 } 9 10 public class TestQuote2 { 11 12 public static void main(String args[]) { 13 Demo2 d1 = new Demo2(); 14 d1.a = 1; 15 16 Demo2 d2 = new Demo2(); 17 d2.a = 2; 18 19 System.out.println(d1.a); 20 System.out.println(d2.a); 21 function(d1, d2); 22 System.out.println(d1.a); 23 System.out.println(d2.a); 24 } 25 26 private static void function(Demo2 d1, Demo2 d2) { 27 Demo2 temp; 28 temp = d1; 29 d1 = d2; 30 d2 = temp; 31 } 32 }
代码1中的输出发生了改变,代码2中的输出没变。究其结果,看内存图中的各参数变化。
图:代码1的内存映像
图:代码2的内存映像
回忆一下我们之前是如何给对象的属性赋值的?
对象名.成员变量名 = 成员变量值
其实Java语言还提供了另外一种方式,帮助我们完成对成员变量(对象属性)的初始化——对象的构造方法
构造方法的作用: 就是使得jvm在构造对象的时候,帮助我们进行成员变量的初始化。
- 构造方法的格式
- 构造方法的注意事项
1 package com.cskaoyan.syntax.constructor; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 回忆一下我们之前是如何给对象的属性赋值的? 8 对象名.成员变量名 = 成员变量值 9 10 类比于,数组的静态初始化方式,那创建对象的时候,是否可以,用某种方式, 11 把对象成员变量的值,初始化成我们想要的初始值。而不要我们对每一个成员变量,手动赋初值 12 13 通过一种,特殊的方法(定义在类中) -> 构造方法,就可以完成以上功能,即创建对象之后, 14 将对象的成员变量的值,初始化成我们想要的值 15 16 构造方法的作用:就是使得jvm在构造对象的时候,帮助我们进行成员变量的初始化。 17 18 19 1. 首先,在类中,定义构造方法语法, 有两个不同于一般方法的地方 20 a. 构造方法的方法名固定,和类名相同 (这一点,java语言的语法规范,所以构造方法命名,确实违反了驼峰命名规则) 21 b. 构造方法(没有返回值)的方法声明中,没有返回值这一项 22 当一个普通方法没有返回值 void 23 24 注意事项: 25 1. 一个类中可以,定义多个重载的构造方法 26 2. 我们需要知道,其实,当我们没有在类中定义任何一个构造方法的时候, 27 jvm会自动添加一个默认的构造方法(无参构造方法) 28 但是,一旦我们自己在类定义中,定义了哪怕只有一个构造方法,jvm就不会在自动帮我们添加那个无参构造方法了 29 30 jvm为什么这么干? 这和jvm执行固定流程有关系 31 32 开辟对象的存储空间 -> 给对象成员变量赋予默认初值 -> 使用构造方法,初始化对象成员变量的值 33 34 3. 构造方法何时执行呢? 35 由jvm,来真正调用执行的,在创建对象的最后一步,执行构造方法,给对象的成员变量赋予Coder传递的初值 36 * 37 */ 38 public class Demo1 { 39 40 public static void main(String[] args) { 41 // 类比数组引出构造方法 42 //introduce(); 43 44 45 //使用四参构造方法 46 //Student zhangsan = new Student("zhangsan", false, 18, 1); 47 //System.out.println(zhangsan.name + "--" + zhangsan.isMale + "--" + zhangsan.age + "--" + zhangsan.sno); 48 49 //使用2参构造方法 50 //Student lisi = new Student("lisi", 20); 51 //// lisi--false--20--0 52 //System.out.println(lisi.name + "--" + lisi.isMale + "--" + lisi.age + "--" + lisi.sno); 53 54 55 //以最简单的方式,不对成员变量值初始化的方式 56 //其实这种方式,就等价于,告诉jvm使用无参构造方法,来初始化成员变量的值 57 Student stu = new Student(); 58 System.out.println(stu.name + "--" + stu.isMale + "--" + stu.age + "--" + stu.sno); 59 } 60 61 private static void introduce() { 62 //类比于数组 63 int[] arr = new int[3]; 64 arr[0] = 1; 65 arr[1] = 2; 66 arr[2] = 3; 67 68 //数组,还有一种初始化数组元素值的方式 69 int[] arr1 = {1, 2, 3}; 70 } 71 72 } 73 74 /* 75 定义学生类,来描述所有学生对象,共有的属性和行为 76 */ 77 class Student { 78 79 // 姓名 80 String name; 81 82 //性别 83 boolean isMale; 84 85 //年龄 86 int age; 87 88 //学号 89 int sno; 90 91 ////不接收任何参数,无参构造方法 92 public Student() { 93 name = "未知"; 94 isMale = true; 95 age = -1; 96 sno = -1; 97 System.out.println("Student()"); 98 } 99 100 // 定义一个接收两个参数的,构造方法 101 public Student(String nameValue, int ageValue) { 102 System.out.println("Student(String nameValue, int ageValue)"); 103 name = nameValue; 104 age = ageValue; 105 } 106 107 //定义构造方法 用来初始化,对象中成员变量的值 108 public Student(String nameValue, boolean isMaleValue, int ageValue, int snoValue) { 109 System.out.println("Student(String nameValue, boolean isMaleValue, int ageValue, int snoValue)"); 110 name = nameValue; 111 isMale = isMaleValue; 112 age = ageValue; 113 sno = snoValue; 114 } 115 116 117 118 119 // 学生具有的吃饭行为 120 public void eat() { 121 System.out.println(name + "吃饭"); 122 } 123 124 125 //睡觉行为 126 public void sleep() { 127 System.out.println("sleeping"); 128 } 129 130 //学习 131 public void study() { 132 System.out.println(sno + " 在学习"); 133 } 134 135 }
this关键字:代表对象自身的引用
this关键字的作用:
- 解决成员变量的隐藏的问题
- 访问对象的成员
- 访问对象的构造方法
1 package com.cskaoyan.syntax.keythis; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/14. 5 * @version 1.0 6 * 7 * 成员变量的隐藏问题: 8 * 1. 方法中定义了和类中同名的成员变量 9 * 2. 在方法体中,通过通过同名变量的,变量名来访问变量值,我只能访问到方法中的, 10 * 那个局部同名变量的值,而访问不到,同名成员变量的值 11 * 3. 在方法体中,就好像同名成员变量,被同名局部变量给隐藏起来了 12 * 13 * 如何解决成员变量的隐藏问题呢? 核心,就是在方法体中,以某种方式,区分同名成员变量和同名局部变量 14 * 引入一个关键字 this, 帮助我们解决了,成员变量的隐藏问题 15 * 16 * 正式学习this关键字 17 * this关键字:代表 (对象自身)当前对象 的引用 18 * 当前对象: 19 * a. 在构造方法中的 this,指代的当前对象,其实就是,构造方法执行时,jvm正在创建的那个对象 20 * b. 在普通成员方法的方法体中,this,指代的对象是谁? 21 * 对于普通成员方法而言 对象名.方法(), 22 * 所以对于普通成员方法而言,在到那个对象上掉调用方法,方法中的this就指的是哪个对象 23 * 24 * this关键字的作用: 25 1. 解决成员变量的隐藏的问题 26 2. 访问对象的成员(访问当前对象的成员变量值,访问当前对象的成员方法) 27 3. 访问对象的构造方法 this(实参列表) 在某个构造方法中,调用其他构造方法 28 a. this调用构造方法,这个代码,只能某个构造方法的方法体中 29 b. this调用构造方法, 必须处在构造方法的第一条语句的位置 30 31 * 32 * 33 */ 34 public class Demo1 { 35 36 public static void main(String[] args) { 37 38 String name = "wuliuqi"; 39 int age = 20; 40 Student stu1 = new Student(name, age); 41 // null--false--0--0 42 //System.out.println(stu1.name + "--" + stu1.isMale + "--" + stu1.age + "--" + stu1.sno); 43 44 Student shisan = new Student("shisan", 18); 45 //对象名.的形式 46 stu1.eat(); // wuliuqi吃饭 47 shisan.eat(); // shisan吃饭 48 49 Student student = new Student(); 50 // 51 } 52 53 } 54 /* 55 定义学生类,来描述所有学生对象,共有的属性和行为 56 */ 57 class Student { 58 59 // 姓名 60 String name; 61 62 //性别 63 boolean isMale; 64 65 //年龄 66 int age; 67 68 //学号 69 int sno; 70 71 // 在无参构造方法中,给name,age成员赋予我们自己的默认初值 72 public Student() { 73 //this.name ="未知"; 74 //this.age = -1; 75 76 //在构造方法中,调用其他构造方法 77 this("未知", -1); 78 79 } 80 81 // 成员变量的隐藏问题 82 // 定义一个接收两个参数的,构造方法 83 public Student(String name, int age) { 84 85 System.out.println(name); 86 this.name = name; 87 this.age = age; 88 System.out.println("constructor"); 89 } 90 91 // 学生具有的吃饭行为 92 public void eat() { 93 System.out.println(this.name + "吃饭"); 94 95 //调用普通成员方法 96 this.test(); 97 } 98 99 public void test() { 100 101 } 102 103 }
static关键字
- static引例
1 package com.cskaoyan.keystatic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/15. 5 * @version 1.0 6 */ 7 public class Demo1Intro { 8 9 public static void main(String[] args) { 10 11 Student stu1 = new Student("张三", true, 18, 1); 12 //手动给stu1对象的,schoolName成员变量赋予初值 13 stu1.schoolName = "王道码农训练营"; 14 15 Student stu2 = new Student("李四", false, 20, 2); 16 //手动给stu1对象的,schoolName成员变量赋予初值 17 //stu1.schoolName = "王道训练营"; 18 System.out.println(stu2.schoolName); 19 20 } 21 22 } 23 24 /* 25 假设,该Student类描述 同一所学校 的所有 学生对象 26 */ 27 class Student { 28 29 // 姓名 30 String name; 31 32 //性别 33 boolean isMale; 34 35 //年龄 36 int age; 37 38 //学号 39 int sno; 40 41 //学校名称 用static关键字修饰 42 static String schoolName; 43 44 public Student(String name, boolean isMale, int age, int sno) { 45 this.name = name; 46 this.isMale = isMale; 47 this.age = age; 48 this.sno = sno; 49 } 50 51 // 学生具有的吃饭行为 52 public void eat() { 53 System.out.println(name + "吃饭"); 54 } 55 56 57 //睡觉行为 58 public void sleep() { 59 System.out.println("sleeping"); 60 } 61 62 //学习 63 public void study() { 64 System.out.println(sno + " 在学习"); 65 } 66 67 }
图:static引例
1 package com.cskaoyan.keystatic; 2 3 import java.util.Arrays; 4 5 /** 6 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/15. 7 * @version 1.0 8 * 9 * static关键字:可以修饰普通 成员变量 和 普通成员方法 10 * 说明一下: 按照严格意义上的面向对象思想, static修饰成员变量,和static修饰成员方法, 11 * 都不能算做类中定义的成员 12 * 13 * 我们之所以称static修饰的变量为 静态成员变量(习惯上的称呼) 14 * 方法为 静态成员方法(习惯上的称呼) 15 * 16 static关键字的特点: 17 1. 被类的所有 对象所共享:(判定是否使用static的关键) 18 a. 当static修饰了成员变量,该成员变量变量的值,就不在存储与对象中了, 19 而是,单独存储了一份,被类的所有对象所共享 20 21 b. 当static修饰成员方法的时候,该方法被当前类的所有对象共享 22 当前类对象.方法(和普通成员方法从共享的角度,几乎没有太大区别) 23 24 2. 可以通过类名访问 25 a. 可以通过类名直接访问,static成员变量的值 26 b. 直接通过类名直接调用,static成员方法 27 28 3. 随着类的加载而加载 29 a. static成员变量,随着类加载过程,其实就已经在方法区中,分配了内存 30 b. static成员方法, 一旦类加载完毕,我们就可以直接访问,static方法,而不必等待,创建对象,然后在对象名. 访问方法 31 32 4. 优先于对象而存在 33 1. 不依赖于对象而存在 34 a. 成员变量的角度来理解,static修饰的成员变量,不在依赖于对象而存在,因为static修饰的成员变量的值, 35 不在存储在,该类的每个对象中 36 作为对比,没有被static修饰的成员变量,都依赖于对象而存在,因为他们的值,都存储在对象中 37 38 b. 成员方法角度,被static修饰的成员方法,在没有对象存在的情况下,也可以直接通过类名来调用方法 39 ,作为对比,没有被static修饰的,普通成员方法,它依赖于对象而存在, 原因是,普通成员方法中, 40 可以访问,普通成员变量的值,而普通对象的值又是依赖于对象而存在的 41 42 2. 先出现在内存 43 静态成员变量, 一定 先于 没有被static修饰的普通成员变量,出现在内存中 44 45 注意事项: 46 a. 非静态成员变量(非静态的成员方法),不能在静态上下文(静态方法的方法体)中被访问 47 为什么? 48 1) 之所以静态方法,的方法体中,不能访问非静态成员变量,因为静态方法,优先于对象而存在 49 首先,我们可以在,没有任何对象存在情况下,就可以直接通过类名,运行静态方法, 50 所以静态方法,是访问不到当前对象的普通成员变量值(因为此时,当前对象不存在) 51 52 2) 之所以静态方法方法体中,不能访问非静态的成员方法,因为,如果静态方法可以直接调用非静态的的方法 53 非静态的方法,可以去访问当前对象的成员变量值,而当前对象,此时有能还不存在 54 55 b. 静态方法 or 非静态方法,方法体中都不能使用static关键字定义变量 56 57 c. 静态方法的使用场景 58 静态方法和非静态方法除了访问方式不同, 59 最大的区别,就是静态方法可以访问到的数据集合和非静态的方法不同(静态方法方法体中,不能直接访问非静态的成员变量) 60 所有,通常,静态方法它所访问的数据:要么是静态的成员变量,要么是方法的形式参数 61 62 通常,定义静态方法,都是为了,方别使用该方法的功能(工具方法), 让别人使用该方法的功能, 63 为了方便使用该方法,通常,将这种工具方法定义为静态方法(访问的数据通常是方法参数),不用创建对象就可以使用 64 65 */ 66 public class Demo2 { 67 68 public static void main(String[] args) { 69 //StaticClass staticClass = new StaticClass(); 70 //1. 被类的所有 对象所共享:(判定是否使用static的关键) 71 //静态成员变量的值,被类的所有对象共享 72 //System.out.println(staticClass.staticField); 73 ////静态成员方法,被类的所有对象共享 74 //staticClass.testStatic(); 75 76 77 // 2. 可以通过类名访问 78 System.out.println(StaticClass.staticField); 79 StaticClass.testStatic(); 80 81 //3. 在不创建对象的情况下,通过类名,直接访问static成员变量或者成员方法,也会触发该类的类加载 82 //StaticClass.testStatic(); 83 84 int[] arr = {1, 2, 3}; 85 System.out.println(Arrays.toString(arr)); 86 } 87 88 } 89 90 91 class StaticClass { 92 93 //定义静态成员变量 94 static int staticField = 100; 95 96 double doubleValue = 9.9; 97 98 99 public static void testStatic() { 100 System.out.println("testStatic"); 101 102 //被static修饰的静态方法体中,访问一下没有被static普通成员变量 103 //System.out.println(this.doubleValue); 104 //被static修饰的静态方法体中,访问一下没有被static普通成员方法 105 //this.testMethod(); 106 107 //可以访问静态的成员变量,可以调用静态的方法 108 test(); 109 System.out.println(staticField); 110 111 112 } 113 public void testMethod() { 114 System.out.println("testMethod " + this.doubleValue); 115 116 //可以直接访问, 静态方法和静态变量 117 System.out.println(staticField); 118 testStatic(); 119 } 120 121 public static void test() { 122 123 } 124 }
图:static修饰的成员变量存储在方法区
1 package com.cskaoyan.keystatic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/15. 5 * @version 1.0 6 * 7 * 静态成员变量和普通成员变量: 8 所属不同 9 静态变量属于类,所以也称为为 类变量 10 成员变量属于对象,所以也称为 实例变量(对象变量) 11 12 内存中的位置不同 13 静态变量存储于方法区的静态区 14 成员变量存储于堆内存 15 16 内存出现时间不同 17 静态变量随着类的加载而加载,随着类的消失而消失 18 成员变量随着对象的创建而存在,随着对象的消失而消失 19 20 调用不同 21 静态变量可以通过类名调用,也可以通过对象调用 22 成员变量只能通过对象名调用 23 24 */ 25 public class Demo3 { 26 27 }
面向对象思想