面向对象
一、面向过程:当需要实现一个功能时 每一个具体的步骤都要亲力亲为 详细处理每一个细节
二、面向对象:当需要实现一个功能时 不关心具体的步骤 而是找一个已经具有该功能的人来帮我做事
三、面向对象三大基本特征:封装 继承 多态
四、类:是一组相关属性和行为的集合 可以看成是一类事物的模板 使用事物的属性特征和行为特征来描述该类事物
- 属性:该事物的状态信息
- 行为:该事物能够做什么
五、对象:是一类事物的具体体现 对象是类的一个实例 必须具备该类事物的属性和行为
六、类与对象的关系:
- 类是对一类事物的描述 是抽象的
- 对象是一类事物的实例 是具体的
- 类是对象的模板 对象是类的实体
七、局部变量和成员变量的区别:
- 定义的位置不一样:
局部变量:在方法内部
成员变量:在方法的外部 直接写在类当中 - 作用范围不一样:
局部变量:只有方法当中才可以使用 出了方法就不能再用
成员变量:整个类全都可以通用 - 默认值不一样:
局部变量:没有默认值 如果要想使用 必须手动进行赋值
成员变量:如果没有赋值 会有默认值 规则和数组一样 - 内存的位置不一样:
局部变量:位于栈内存
成员变量:位于堆内存 - 生命周期不一样:
局部变量:随着方法进栈而诞生 随着方法出栈而消失
成员变量:随着对象进栈而诞生 随着对象被垃圾回收而消失
【注】:方法的形式参数是局部变量 该参数在方法调用的时候必然会被赋值的
import java.util.Arrays;
public class PrintArray {
public static void main(String[] args) {
int[] array = {10,20,30,40,50};
//要求打印格式为:[10,20,30,40,50]
//使用面向过程 每个步骤细节都要亲力亲为
System.out.print("[");
for(int i = 0;i < array.length;i++) {
if (i == array.length - 1) {//如果是最后一个元素
System.out.println(array[i] + "]");
}else {//如果不是最后一个元素
System.out.print(array[i] + ",");
}
}
System.out.println("================================");
//使用面向对象 找一个jdk给我们提供好的Array类 其中有一个toString方法 直接就能把数组变成想要的格式的字符串
System.out.print(Arrays.toString(array));
}
}
类与对象的使用
一、定义一个类 用来模拟“学生”事物 其中有两个组成部分:
- 属性(是什么):姓名 年龄
- 行为(能做什么):吃饭 睡觉 学习
二、对应到Java的类当中:
- 成员变量(属性):
String name;//姓名
int age;//年龄
- 成员方法(行为):成员方法最重要的特征是 没有static
public void eat(){}//吃饭
public void sleep(){}//睡觉
public void study(){}//学习
【注】:
- 成员变量直接定义在类当中 方法外
- 成员方法不要写static关键字
public class Student {
//成员变量:直接写在class内 方法外的变量
String name;//姓名
int age;//年龄
//成员方法
public void eat(){
System.out.println("吃饭!");
}
public void sleep(){
System.out.println("睡觉!");
}
public void study(){
System.out.println("学习!");
}
}
三、通常情况下 一个类并不能直接使用 需要根据类创建一个对象 才能使用
- 如何创建对象:
(1)导包:指出需要使用的类在什么位置
格式:import 包名称.类名称;
对于和当前类属于同一个包的情况 可以省略导包语句不写
(2)创建对象:
格式:类名称 对象名 = new 类名称();
(3)使用:
使用成员变量:对象名.成员变量名
使用成员方法:对象名.成员方法名([参数])
也就是想用谁就用对象名.谁
【注】:如果成员变量没有进行赋值 那么将会有一个默认值 规则和数组一样
public class StudentUse2 {
public static void main(String[] args) {
//1.导包
//我需要使用的Student类和我自己位于同一个包下 所以省略导包语句不写
//2.创建:
//格式:类名称 对象名 = new 类名称();
//根据Student类创建一个名为stu的对象
Student stu = new Student();
//3.使用其中的成员变量
//格式:对象名.成员变量名
System.out.println(stu.name);//null
System.out.println(stu.age);//0
System.out.println("===================================");
//改变对象当中的成员变量数值内容
//将右侧的字符串赋值交给stu对象当中的name成员变量
stu.name = "赵丽颖";
stu.age = 18;
System.out.println(stu.name);//赵丽颖
System.out.println(stu.age);//18
System.out.println("===================================");
//4.使用对象的成员方法
//格式:对象名.成员方法名([参数])
stu.eat();
stu.sleep();
stu.study();
}
}
四、一个标准的类通常要拥有下面四个组成部分:(这样标准的一个类也叫Java bean)
- 所有的成员变量都要使用private关键字修饰
- 为每一个成员变量编写一对getter setter方法
- 编写一个无参数的构造方法
- 编写一个全参数的构造方法
public class Student2 {
private String name;//姓名
private int age;//年龄
public Student2() {
super();
}
public Student2(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
五、构造方法:专门用来创建对象的方法 当我们通过关键字new来创建对象时 其实就是在调用构造方法
格式:
public 类名称(参数类型 参数名称,……){
方法体;
}
【注】:
- 构造方法的名称必须和所在的类名称完全一样 就连大小写也要一样
- 构造方法不要写返回值类型 连void都不要写
- 构造方法不能return一个具体的返回值
- 如果没有编写任何构造方法 那么编译器就会默认赠送一个构造方法 没有参数 方法体什么也不做 即:
public ConstructorStudent() {}
- 一旦编写了至少一个构造方法 那么编译器将不再赠送
- 构造方法可以进行重载
- 全参和无参构造方法可以方便我们在创建对象时 把现成的数据通过参数的形式设置进来 省去多次调用setter getter方法的麻烦
- 当需要修改对象内容时 即重新传递数据时 还是需要用到setter getter方法
public class ConstructorStudent {
//成员变量
private String name;
private int age;
//无参数的构造方法
public ConstructorStudent() {
System.out.println("无参构造方法执行啦!");
}
//有参数的构造方法
public ConstructorStudent(String name,int age) {
System.out.println("有参构造方法执行啦!");
this.setName(name);
this.setAge(age);
}
//getter setter
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 class ConstructorStudentUse {
public static void main(String[] args) {
ConstructorStudent stu = new ConstructorStudent();//无参构造方法
ConstructorStudent stu2 = new ConstructorStudent("赵丽颖",20);//有参构造方法
System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
//如果需要改变对象当中的成员变量数据内容 仍然还需要使用setter方法
stu2.setAge(21);
System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
}
}
封装
一、封装:将一些细节信息隐藏起来 对于外界不可见
二、封装的两种形式:
- 方法就是一种封装
- 关键字private也是一种封装
三、问题描述:定义Person的年龄时 无法阻止不合理的数值被设置进来 如:-20
四、解决方法:用private关键字将需要保护的成员变量进行修饰。
五、一旦使用了private进行修饰 那么本类当中仍然可以随意访问 但是 超出了本类范围就不能再直接访问了 可以间接访问。
六、间接访问private成员变量 就是定义一对getter/setter方法
【注】:
- setter方法必须含有形参 且形参的数据类型和要设置的成员变量的数据类型一致
- getter方法不能含有参数 返回值的数据类型和要获取的成员变量的数据类型一致
- 必须叫getXxx或者setXxx
- 对于基本数据类型当中的boolean值 getter方法一定要写成isXxx的形式 而setXxx规则不变
public class PrivatePerson {
String name;
private int age;
public void show() {
System.out.println("我叫:" + name + ",年龄:" + age);
}
//这个成员方法专门用于向age设置数据
public void setAge(int num) {//该成员方法的名称必须以set开头 且后面紧跟首字母大写的成员变量
if(num < 100 && num >=0) {
age = num;
}else {
System.out.println("数据不合理!");
}
}
//这个成员方法专门用于获取age的数据
public int getAge() {//该成员方法的名称必须以get开头 且后面紧跟首字母大写的成员变量
return age;
}
}
七、当方法的局部变量和类的成员变量重名时 根据“就近原则” 优先使用局部变量,如果需要访问本类当中的成员变量 需要使用格式:this.成员变量名
八、“通过谁调用的方法 谁就是this”:stu调用的setName setAge setMale 所以stu就是this
public class PrivateStudent {
private String name;
private int age;
private boolean male;//是不是男的
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 boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
}
}
使用时:
public class PrivateStudentUse {
public static void main(String[] args) {
PrivateStudent stu = new PrivateStudent();
stu.setName("鹿晗");
stu.setAge(20);
stu.setMale(true);
System.out.println("姓名:" + stu.getName());
System.out.println("年龄:" + stu.getAge());
System.out.println("是不是男:" + stu.isMale());
}
}
匿名对象
一、创建对象的标准格式:类名称 对象名 = new 类名称();
二、匿名对象就是只有右边的对象 没有左边的“类名称 对象名 =
”
三、匿名对象的格式:new.类名称
【注】:匿名对象只能使用唯一的一次 下次再用就要再创建一个新对象
四、使用建议:如果确定有一个对象只需要使用唯一一次就可以使用匿名对象
import java.util.Scanner;
public class Anonymous {
public static void main(String[] args) {
//创建对象的标准格式
AnonymousPerson one = new AnonymousPerson();//左边one就是对象的名字
one.name = "高圆圆";
one.showName();//我叫高圆圆
System.out.println("匿名对象的使用:======================================");
//匿名对象
new AnonymousPerson().name = "赵又廷";
new AnonymousPerson().showName();//我叫null
/*为啥这里不输出“我叫赵又廷”:
* 在这个类里面有三个new 也就创建了三个不同的对象
* “赵又廷”是给第二个对象的name赋的值 而我们并没有给第三个对象的name赋值 所以 第三个对象打印null
*/
System.out.println("匿名对象在Scanner中的应用:======================================");
//Scanner的普通使用方式
Scanner sc1 = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int num1 = sc1.nextInt();
System.out.println("输入的第一个数字:" + num1);
//Scanner的匿名对象使用方式
System.out.println("请输入第二个数字:");
int num2 = new Scanner(System.in).nextInt();
System.out.println("输入的第二个数字:" + num2);
System.out.println("匿名对象作为方法的参数:======================================");
//使用一般写法传入参数
System.out.println("请输入第三个数字:");
Scanner sc2 = new Scanner(System.in);
methodParam(sc2);
//使用匿名对象来传参
System.out.println("请输入第四个数字:");
methodParam(new Scanner(System.in));
System.out.println("匿名对象作为方法的返回值:======================================");
Scanner sc3 = methodReturn();
int num3 = sc3.nextInt();
System.out.println("输入的第五个数字:" + num3);
}
private static Scanner methodReturn() {
/*一般写法
* Scanner sc4 = new Scanner(System.in);
* return sc4;
*/
//匿名对象作为方法的返回值
return new Scanner(System.in);
}
private static void methodParam(Scanner sc) {
int num = sc.nextInt();
System.out.println("输入的第三/四个数字:" + num);
}
}
ArrayList
一、数组的长度不能发生改变 但是ArrayList集合的长度可以随意改变
二、对于ArrayList来说 有一个尖括号<E>
代表泛型
三、泛型:装在集合当中的所有元素 全都是统一的什么类型
【注】:
- 泛型只能是引用类型 不能是基本类型
- 对于ArrayList集合来说 直接打印得到的不是地址值 而是内容 如果内容是空 得到的是空的中括号:[]
四、如果希望向集合ArrayList当中存储基本类型数据 必须使用基本类型对应的“包装类”
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
char | Character |
boolean | Boolean |
五、从JDK1.5+开始 支持自动装箱 自动拆箱
- 自动装箱:基本类型自动转换为包装类型
- 自动拆箱:包装类型自动转换为基本类型
import java.util.ArrayList;
public class ArrayListBasic {
public static void main(String[] args) {
//正常情况下创建一个ArrayList集合 集合的名称是listA 里面装的全是String字符串类型的数据 只能装引用类型的数据
ArrayList<String> listA = new ArrayList<>();
//ArrayList<int> listB = new ArrayList<>();错误!泛型只能是引用类型 不能是基本类型
//使用包装类定义了一个ArrayList集合 集合的名称是listC 里面装的全是int类型的数据
ArrayList<Integer> listC = new ArrayList<>();
listC.add(100);
listC.add(200);
System.out.println(listC);//[100, 200]
//获取包装类数据
int num = listC.get(1);
System.out.println("第一号元素是:" + num);//200
}
}
六、ArrayList常用方法:
public boolean add(E e)
:向集合中添加元素 参数类型和泛型一致 返回值代表添加是否成功
【注】:对于ArrayList集合来说 add添加动作一定是成功的 所以返回值可用可不用,但是对于其他集合(今后学习)来说 add添加动作不一定成功
public E get(int index)
:从集合中获取元素 参数是索引编号 返回值就是对应位置的元素 E是泛型public E remove(int index)
:从集合中删除元素 参数是索引编号 返回值就是被删除的元素public int size()
:获取集合的尺寸长度 返回值是集合中包含的元素个数
Scanner类
一、Scanner类的功能:可以实现键盘输入数据到程序中
二、引用类型的一般步骤:
- 导包:
import 包路径.类名称
【注】:
- 如果需要使用的目标类和当前类位于同一个包下 则可以省略导包语句不写
- 只有
Java.lang
包下的内容不需要导包 其他的包都需要import语句
- 创建:
类名称 对象名 = new 类名称([参数]);
- 使用:
对象名.成员方法名([参数]);
三、获取键盘输入的一个int数字:int num = sc.nextInt();
四、获取键盘输入的一个字符串:String str = sc.next();
五、其实键盘输入的全都是字符串 而nextInt把字符串化成了int数字展示出来 而next则是直接把键盘上输入的字符串拿过来直接显示
import java.util.Scanner;//1.导包
public class ScannerClass {
public static void main(String[] args) {
//2.创建
Scanner sc = new Scanner(System.in);//System.in表示从键盘输入
//3.使用
int num = sc.nextInt();//获取键盘输入的一个int数字
System.out.println("输入的int数字是:" + num);
System.out.println("==================================================");
String str = sc.next();//获取键盘输入的一个字符串
System.out.println("输入的字符串是:" + str);
}
}
Random类
Random类用来生成随机数字 使用起来也是三个步骤:
- 导包:
import java.util.Random;
- 创建:
Random r = new Random();//小括号中留空即可
- 使用:
(1)获取一个随机的int数字(范围是int所有范围 由正负两种):int num = r.nextInt();
(2)获取一个随机的int数字(参数代表了范围 左闭右开区间):int num = r.nextInt(3);
实际上代表的含义是[0,3) 也就是0-2
import java.util.Random;
public class RandomClass {
public static void main(String[] args) {
Random r1 = new Random();
int num1 = r1.nextInt();
System.out.println("随机数是:" + num1);//-1565682014 1106506546
Random r2 = new Random();
for(int i = 0;i < 100;i++) {
int num2 = r2.nextInt(10);//生成0-9的随机整数
System.out.println("随机数是:" + num2);
}
}
}
String类
一、java.lang.String
类代表字符串
二、API当中说 Java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现,其实就是说,程序当中所有的双引号字符串都是String类的对象 就算没有new 也照样是
三、字符串的特点:
- 字符串的内容永不可变
- 正是因为字符串不可改变 所以字符串是可以共享使用的
- 字符串效果上相当于是char[]字符数组 但是底层原理是byte[]字节数组
- 创建字符串的常见3+种方式:
(1)三种构造方法:(new就是在调用构造方法)
public String()
:创建一个空白字符串 不含有任何内容
public String(char[] array)
:根据字符数组的内容来创建对应的字符串
public String(byte[] array)
:根据字节数组的内容来创建对应的字符串
(2)一种直接创建:String str = "Hello";//右边直接用双引号
【注】:直接写上双引号 就是字符串对象
public class StringClass {
public static void main(String[] args) {
//使用空参构造
String str1 = new String();//小括号留空说明字符串什么内容都没有
System.out.println("第一个字符串:" + str1);//第一个字符串:
//根据字符数组的内容来创建对应的字符串
char[] charArray = {'a','b','c'};
String str2 = new String(charArray);
System.out.println("第二个字符串:" + str2);//第二个字符串:abc
//根据字节数组的内容来创建对应的字符串
byte[] byteArray = {97,98,99};
String str3 = new String(byteArray);
System.out.println("第三个字符串:" + str3);//第三个字符串:abc
//直接创建
String str4 = "Hello";
System.out.println("第四个字符串:" + str4);//第四个字符串:Hello
}
}
字符串转换
String当中与转换相关的常用方法有:
public char[] toCharArray()
:将当前字符串拆分成为字符数组作为返回值public byte[] getBytes()
:获得当前字符串底层的字节数组public String replace(CharSequence oldString,CharSequence newString)
:将所有出现的老字符串替换成新的字符串 返回替换后的结果新字符串CharSequence
:接口 可以接收字符串类型
public class StringConvert {
public static void main(String[] args) {
//转换成为字符数组
char[] chars = "Hello".toCharArray();
System.out.println(chars[0]);//H
System.out.println(chars.length);//5
System.out.println("=================================================");
//转换成为字节数组
byte[] bytes = "abc".getBytes();
for(int i = 0;i < bytes.length;i++) {
System.out.println(bytes[i]);//97 98 99
}
System.out.println("==================================================");
//
String str1 = "How do you do?";
String str2 = str1.replace("o", "*");
System.out.println(str1);//How do you do?
System.out.println(str2);//H*w d* y*u d*?
}
}
字符串获取
String中与获取相关的常用方法:
public int length()
:获取字符串当中含有的字符个数 拿到字符串长度public String cancat(String str)
:将当前字符串和参数字符串拼接成为返回值新的字符串public char charAt(int index)
:获取指定索引位置的单个字符 索引从0开始public int indexOf(String str)
:查找参数字符串在本字符串当中首次出现的索引位置 如果没有 返回-1值
public class StringGet {
public static void main(String[] args) {
//获取字符串的长度
System.out.println("获取字符串长度:======================================");
int length = "asdfghjkloivf".length();
System.out.println("字符串的长度是:" + length);//字符串的长度是:13
//拼接字符串
System.out.println("拼接字符串:====================================");
String str1 = "Hello";
String str2 = "World";
String str3 = str1.concat(str2);
System.out.println(str1);//Hello
System.out.println(str2);//World
System.out.println(str3);//HelloWorld
//获取指定索引位置的单个字符
System.out.println("获取指定索引位置的单个字符:=====================================");
char ch = "Hello".charAt(1);
System.out.println("在1号索引位置的字符是:" + ch);//在1号索引位置的字符是:e
//查找参数字符串在本来字符串当中出现的第一次索引位置 如果根本没有 返回-1
String original = "HelloWorldHelloWorld";
int index = original.indexOf("llo");
System.out.println("第一次索引值是:" + index);//第一次索引值是:2
System.out.println("HelloWorld".indexOf("abc"));//-1
}
}
字符串分割
分割字符串的方法:public String[] split(String regex)
:按照参数的规则将字符串切分成若干部分并保存在String数组中
注意事项:split方法的参数其实是一个正则表达式 所以如果按照英文句点“
.
”切分必须写“\\.
”
public class StringSplit {
public static void main(String[] args) {
String str1 = "aaa,bbb,ccc";
String[] array1 = str1.split(",");
for(int i = 0;i < array1.length;i++) {
System.out.println(array1[i]);
}
System.out.println("==================================================");
//按照“.”进行切割 会报错
String str2 = "aaa.bbb.ccc";
String[] array2 = str1.split(".");
for(int i = 0;i < array1.length;i++) {
System.out.println(array2[i]);
}
}
}
字符串截取
字符串的截取方法:
public String substring(int index)
:截取从参数位置一直到字符串末尾 返回新字符串public String substring(int begin,int end)
:截取从begin开始 一直到end结束 中间的字符串
备注:[begin,end) 包含左边 不包含右边
public class Substring {
public static void main(String[] args) {
String str1 = "HelloWorld";
String str2 = str1.substring(5);
System.out.println(str1);//HelloWorld
System.out.println(str2);//World
System.out.println("===================");
String strA = "Hello";
System.out.println(strA);//Hello
strA = "Java";
System.out.println(strA);//Java
/*
* 不是说字符串中保存的内容不变吗?为什么这里变了:
* 注意 上面这种写法 字符串的内容仍然是没有改变的
* 上面有;两个字符串:"Hello" "Java"
* strA中保存的是地址值 本来地址值是Hello的0x666 后来地址值变成了Java的0x999
*/
}
}
字符串常量池
一、字符串常量池:程序当中直接写上的双引号字符串就在字符串常量池中 new的不在池中
二、==
:
- 对于基本类型来说
==
是进行数值的比较 - 对于引用类型来说
==
是进行【地址值】的比较
public class ArrayPool {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
char[] charArray = {'a','b','c'};
String str3 = new String(charArray);
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//false
System.out.println(str3 == str2);//false
}
}
三、分析:
- str1 str2:程序会用底层的byte数组来表示字符串“abc” 假设表示字符串“abc”的底层byte数组的地址值为0x333,在堆中有一个字符串常量池 程序当中直接写上的双引号字符串就在字符串常量池中 ,所以该常量池存放字符串对象“abc”所在的byte数组的地址值0x333,在栈中str1和str2保存的都是字符串常量池中保存0x333的地址值 假设是0x666
- str3:定义了一个字符数组(在堆中)假设该字符数组在堆中的地址为0x999 charArray保存的就是0x999,当
new String(charArray)
时 程序会先把字符数组转换成底层byte数组{"97","98","99"}
,假设该byte数组的地址值为0x111 再new上一个字符串对象 该对象保存0x111 假设字符串对象所在地址为0x222,在栈中str3保存的就是0x222
四、==
是进行对象的地址值比较 如果确实需要字符串的内容比较 可以使用以下两个方法:
public boolean equals(Object obj)
:参数可以是任何对象
只有参数是一个字符串并且内容相同的才会给true 否则返回false
【注】:
- 任何对象都能用Object进行接收
- equals方法具有对称性 也就是a.equals(b)和b.equals(a)效果一样
- 如果比较双方一个常量一个变量推荐把常量字符串写在前面 推荐:
"abc".equals(str1)
不推荐:str1.equals(“abc”),因为可能出现空指针异常
public boolean equalsIgnoreCase(String str)
:忽略大小写进行内容的比较
public class ArrayEquals {
public static void main(String[] args) {
System.out.println("不区分大小写比较");
String str1 = "abc";
String str2 = "abc";
char[] charArray = {'a','b','c'};
String str3 = new String(charArray);
System.out.println(str1.equals(str2));//true
System.out.println(str1.equals(str3));//true
System.out.println(str3.equals(str2));//true
System.out.println("abc".equals(str1));//true
System.out.println("ABC".equals(str2));//false
System.out.println("为什么常量不能写在后面:");
String str4 = null;
System.out.println("abc".equals(str4));//推荐 false
//System.out.println(str4.equals("abc"));//不推荐 会报错:空指针异常
System.out.println("区分大小写比较");
String strA = "JAVA";
String strB = "java";
System.out.println(strA.equals(strB));//false 严格区分大小写
System.out.println(strA.equalsIgnoreCase(strB));//true 忽略大小写
//注意:只有英文字母区分大小写 其他都不区分大小写
System.out.println("一".equalsIgnoreCase("壹"));//false
}
}
Math类
java.util.Math是数学相关的工具类 里面提供了大量的静态方法 完成与数学运算相关的操作
public static double abs(double num)
:获取绝对值public static double ceil(double num)
:向上取整public static double floor(double num)
:向下取整public static long round(double num)
:四舍五入Math.PI
代表近似的圆周率常量(double)
静态代码块
一、定义一个学生类 包含:姓名 年龄 学号 所在教室。以此产生了三个学生对象 拥有不同的姓名 年龄 学号 但是他们是同班同学 所以所在教室一样 这应该是多个对象共享同一份数据,如果给每个学生对象都设置所在教室为101 那么如果我要他们全部换到103教室 就需要对每个对象单独更改 要更改三次,如果我们在类中使用static关键字 就可以只在学生类中保存一份所在教室 所有本类对象共享这一份,一旦用了static关键字 那么这样的内容不再属于对象自己 而是属于类的 所以所有本类对象共享这一份
public class Student {
private String name;
private int age;
static String room;// 存放在方法区的静态区,仅此一份,全类对象共享这一份
private int id;
private static int idCounter = 0;//学号计数器 每当new了一个新对象 计数器+1
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
this.setId(++idCounter);
}
public Student() {
super();
this.setId(++idCounter);
}
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 int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
二、内存中:在方法区中有个静态区 用来存放static关键字定义的数据,当编译器看到oneStudent.room
就会去方法区中找static String room
变量 该变量中存储的是静态区中"101教室"存储的地址值
【注】:根据类名称访问静态成员变量时 全程和对象没关系 只和类有关系
import javax.sound.midi.Soundbank;
public class StaticClass {
public static void main(String[] args) {
Student oneStudent = new Student("郭靖",19);
oneStudent.room = "101教室";
System.out.println("姓名:" + oneStudent.getName() + ",年龄:" + oneStudent.getAge()
+ ",所在教室:" + oneStudent.room + ",学号:" + oneStudent.getId());
//姓名:郭靖,年龄:19,所在教室:101教室,学号:1
Student twoStudent = new Student("黄蓉",16);
System.out.println("姓名:" + twoStudent.getName() + ",年龄:" + twoStudent.getAge()
+ ",所在教室:" + oneStudent.room + ",学号:" + twoStudent.getId());
//姓名:黄蓉,年龄:16,所在教室:101教室,学号:2
}
}
三、一旦使用static修饰成员方法 那么这就成为了静态方法 静态方法不属于对象 而是属于类,如果没有static关键字 那么必须先创建对象 然后通过对象才能使用他,如果有了static关键字 那么不需要创建对象 直接就能通过类名称来使用他,无论是成员变量 还是成员方法 如果有了static 都推荐使用类名称进行调用
四、静态变量:类名称.静态变量
五、静态方法:类名称.静态方法([参数])
【注】:
- 静态只能直接访问静态 不能直接访问非静态 因为在内存中是先有的静态内容 后有的非静态内容。“先人不知道后人 但是后人知道先人”
- 静态方法中不能用this 因为this代表当前对象 通过谁调用的方法 谁就是当前对象 而静态跟对象无关
public class StaticMethod {
public static void main(String[] args) {
MyClass objClass = new MyClass();//首先创建对象
objClass.method();//然后才能使用没有static关键字的内容
//对于静态方法来说 可以通过对象名进行调用 也可以直接通过类名称来调用
objClass.methodStatic();//正确 但是不推荐 因为这可能会让人误以为是对象的成员方法
MyClass.methodStatic();//正确 推荐
//对于本类当中的静态方法 可以省略类名称
myMethod();
StaticMethod.myMethod();//两者完全等效
}
private static void myMethod() {
System.out.println("自己的方法!");
}
}
六、直接用一个{}
包起来的都叫代码块
七、静态代码块的格式:
public class 类名称{
static{
//静态代码块的内容
}
}
八、特点:当第一次用到本类时 静态代码块执行唯一的一次 如果再用到本类 静态代码块就不执行了
九、静态内容总是优先于非静态 所以静态代码块比构造方法先执行
十、静态代码块的典型用途:用来一次性地对静态成员变量进行赋值
public class Static {
public static void main(String[] args) {
Person onePerson = new Person();//静态方法先执行
Person twoPerson = new Person();//静态方法不执行 只执行构造方法
}
}
Person类:
public class Person {
static {
System.out.println("静态代码块执行了!");
}
public Person(){
System.out.println("构造方法执行!");
}
}