static关键字
用来修饰的 变量 ,方法 ,代码块,内部类(暂时不涉及),被修饰的成员是属于类的,而不是单单
是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。
类变量
当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
-
静态变量:声明在类中,方法外,且含有static修饰的变量
-
格式:修饰符 static 数据类型 变量名;
-
含义:静态变量不再属于对象本身,而是属于类,静态变量会被通过这个类创建的所有对象所共享
-
调用方式:
类名.静态变量
-
特点:
- 1.静态变量不再属于对象本身,而是属于类,静态变量会被通过这个类创建的所有对象所共享
- 2.静态变量的生命周期,是随着的类的加载而加载,随着类的消失而消失,而且只加载唯一的一次(方法区中的字节码文件一旦有不会加载第二次,继续使用原来的字节码文件)
-
public class StaticDemo01 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("郭靖", 18);
Student s2 = new Student("黄蓉", 16);
Student s3 = new Student("洪七公", 50);
Student.classroom = "射雕";
//打印学生信息
System.out.println(s1.print());
System.out.println(s2.print());
System.out.println(s3.print());
System.out.println("==================================");
//考虑到三位童鞋比较优秀,给他们进行转班
//s1.classroom = "神雕";
Student.classroom = "神雕";
//打印学生信息
System.out.println(s1.print());
System.out.println(s2.print());
System.out.println(s3.print());
}
}
public class Student {
String name;
int age;
static String classroom;
public Student(String name, int age) {
this.name = name;
this.age = age;
//this.classroom = classroom;
}
public Student() {
}
public String print () {
return name + "=" + age + "=" + classroom;
}
}
静态变量的内存图解
三种变量
局部变量:声明在方法内部或者方法声明上的变量
实例变量:声明在类中方法外,且没有static关键字修饰的变量
静态变量:声明在类中方法外,且含有static关键字修饰的变量
区别:
- 代码中的位置不同
- 局部变量:方法内部或者方法声明上
- 实例变量:类中方法外
- 静态变量:类中方法外
- 内存中的位置不同
- 局部变量:栈内存
- 实例变量:堆内存
- 静态变量:堆内存
- 变量的默认值不同
- 局部变量:没有默认值
- 实例变量:有默认值
- 静态变量:有默认值
- 代码中的作用域不同
- 局部变量:方法中
- 实例变量:实例中
- 静态变量:类中
- 内存中的生命周期不同
- 局部变量:随着方法的调用而加载,随着方法的出栈而消失
- 实例变量:随着对象的创建而加载,随着对象的回收而消失
- 静态变量:随着类的加载而加载,随着类的消失而消失
- 变量的加载次数不同
- 局部变量:每调用一次局部变量所在的方法,变量就会加载一次
- 实例变量:每创建一次实例变量所在的对象,变量就会加载一次
- 静态变量:因为字节码文件只加载唯一的一次,静态变量也只加载唯一的一次
练习:
给学生分配学号
public class Student {
private String name;
private int age;
private int id;
private static int num = 210726001;
public Student(String name, int age) {
this.name = name;
this.age = age;
//this.id = num++;
}
public Student() {
//this.id = num++;
}
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;
}
public String print () {
return name + "=" + age + "=" + id;
}
{
this.id = num++;
}
}
public class StaticDemo03 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("迪丽热巴", 18);
Student s2 = new Student("古力娜扎", 20);
Student s3 = new Student("玛尔扎哈", 38);
Student s4 = new Student();
s4.setName("伊戈达拉");
s4.setAge(35);
//打印学生信息
System.out.println(s1.print());
System.out.println(s2.print());
System.out.println(s3.print());
System.out.println(s4.print());
}
}
静态方法
当一个类存在大量工具的实例方法时,如果使用这些实例方法就必须创建对象,而对象除了调用方法没有其它用途,当主方法不结束的话,对象始终在堆内存存在,导致堆内存空间浪费,可以将这些实例方法直接声明为静态方法,声明后方法不在属于对象本身,直接属于类,所以使用方法时不再需要创建对象,直接通过类名进行调用即可
-
格式:
修饰符 static 返回值类型 方法名 () {}
-
调用方式:
类名.静态方法名();
public class ArrayUtils {
public static void printArr (int... arr) {
if (arr == null) {
return;
}
if (arr.length == 0) {
System.out.println("数组:[]");
return;
}
System.out.print("数组:[");
for (int i = 0; i < arr.length; i++) {
if (arr.length - 1 == i) {
System.out.println(arr[i] + "]");
} else {
System.out.print(arr[i] + ", ");
}
}
}
}
public class StaticDemo04 {
public static void main(String[] args) {
/*//创建数组工具类对象
ArrayUtils au = new ArrayUtils();//方法区:ArrayUtils字节码文件 堆内存:ArrayUtils对象
//因为au对象和main()之间存在指向性,只有当main()出栈的时候,au对象才会被认定为垃圾数据
au.printArr(11,22,33);
au.printArr(11,22,33,44);
au.printArr(11,22,33,44,55);*/
ArrayUtils.printArr(11,22,33); //方法区:ArrayUtils字节码文件
ArrayUtils.printArr(11,22,33,44);
ArrayUtils.printArr(11,22,33,44,55);
}
}
静态方法的注意事项
1.当一个类需要声明大量的工具方法时,在将这些工具方法声明为静态的同时,还需要将该类的构造器声明为private,外界就不能再进行对象的创建,直接通过类名调用工具方法
2.实例成员和静态成员的关系:在静态方法中无法调用实例成员,因为静态方法时随着类的加载而加载,实例成员是随着对象的创建而加载,有一种可能,当静态方法加载时,实例成员可能还没有加载(类的加载优先于实例加载)
-
在实例方法中调用实例成员:true
-
在实例方法中调用静态成员:true
-
在静态方法中调用静态成员:true
-
在静态方法中调用实例成员:false
3.在静态方法中无法使用this关键字(和super关键字)
public class StaticDemo05 {
public static void main(String[] args) {
//System.out.println(this);
}
//实例方法
public void method01 () {
//method02();
}
//实例方法
public void method02 () {
method03();
}
//静态方法
public static void method03 () {
//method04();
}
//静态方法
public static void method04 () {
//method01();
}
}
静态代码块
-
位置:成员(类中方法外)
-
格式:
static {
}
-
特点:
- 1.随着类的加载而加载
- 2.只加载唯一的一次
-
作用:
- 1.封装工具类,用来初始化一些变量
- 2.给静态的自定义常量进行初始化(day11讲解)
-
注意事项:
- 1.在静态代码块中无法调用实例成员
- 2.在静态代码块中无法使用this关键字(和super关键字)
public class Utils {
int num = 10;
static {
System.out.println("静态代码块");
//System.out.println(this);
}
{
System.out.println("构造器代码块");
}
}
public class StaticDemo06 {
public static void main(String[] args) {
//创建工具类对象
new Utils();
new Utils();
}
}
单例设计模式
-
单例:单一的实例
-
算法:针对一个过程的优化方案
-
设计模式:解决某一问题专门的一套解决方案
-
框架:半成品项目
单例设计模式:创建唯一对象的一套解决方案
分类:
- 立即加载模式(饿汉式)
- 延迟加载模式(懒汉式)
立即加载模式步骤:
- 1.私有化构造器
- 2.在类中的成员位置声明初始化唯一的对象
- 3.为了可以在外界进行访问,将唯一的对象通过静态进行修饰
- 4.为了唯一对象的安全性,添加封装思想,唯一对象进行私有化
- 5.对外提供"获取"唯一的对象的方法
延迟加载模式步骤:
- 1.私有化构造器
- 2.在类中的成员位置声明唯一的对象
- 3.为了可以在外界进行访问,将唯一的对象通过静态进行修饰
- 4.为了唯一对象的安全性,添加封装思想,唯一对象进行私有化
- 5.对外提供"获取"唯一的对象的方法,当第一次进行获取的时候需要创建对象
public class CEO {
//2.在类中的成员位置创建唯一的对象
//3.为了可以在外界进行访问,将唯一的对象通过静态进行修饰
//4.为了唯一对象的安全性,添加封装思想,唯一对象进行私有化
private static CEO ceo = new CEO();
//1.私有化构造器
private CEO () {}
//5.对外提供"获取"唯一的对象的方法
public static CEO getCEO () {
return ceo;
}
}
public class Boss {
//2.在类中的成员位置声明唯一的对象
//3.为了可以在外界进行访问,将唯一的对象通过静态进行修饰
//4.为了唯一对象的安全性,添加封装思想,唯一对象进行私有化
private static Boss boss;
//1.私有化构造器
private Boss () {}
//5.对外提供"获取"唯一的对象的方法,当第一次进行获取的时候需要创建对象
public static Boss getBoss () {
if (boss == null) {
boss = new Boss();
}
return boss;
}
}
public class StaticDemo07 {
public static void main(String[] args) {
System.out.println(Boss.getBoss());
System.out.println(Boss.getBoss());
System.out.println(Boss.getBoss());
}
}
import关键字
com.atguigu.imp.demo01.ImportDemo01.class
com.atguigu.imp.demo02.Student.class(标准类)
-
含义:导包,将其它包下的类导入到当前包下
-
格式:
import 包名.*;
导入指定包中所有的类
import 包名.类名;
导入指定包中指定的类
-
位置:
package > import > class
API文档
com.atguigu.api.APIDemo.class
JDK_API_1.6_zh_中文.CHM
-
回顾:类的分类:
-
自定义的类
-
API提供的类
-
-
面向对象的学习小技巧:
- 看有没有可以帮助我们做事情对象所对应的类如果有,下一步如果没有,自定义
- 选择合适的构造器创建类的对象
- 通过对象调用方法完成需求
- API文档通过"类文件名字"查询
类文件:class,enum,interface
- 如何查询API文档:
- 类的特点
- 类的位置
- 注意:如果使用的类在java.lang包下,无需导包,除此之外都需要导包,包括在java.lang下面子包都需要进行导包
- 类的构造器
- 类的方法
Scanner类的概述
-
类的特点:针对基本数据类型和字符串类型的文本扫描器工具类
-
类的位置:java.util
-
类的构造器:public Scanner(InputStream source)
- 构造一个新的 Scanner,它生成的值是从指定的输入流扫描的
- 实参:System.in(通过控制台进行键盘录入)
-
类的方法
public byte nextByte()
将输入信息的下一个标记扫描为一个 byte。
public short nextShort()
将输入信息的下一个标记扫描为一个 short。
public int nextInt()
将输入信息的下一个标记扫描为一个 int。
public long nextLong()
将输入信息的下一个标记扫描为一个 long。
public float nextFloat()
将输入信息的下一个标记扫描为一个 float。
public double nextDouble()
将输入信息的下一个标记扫描为一个 double。
public boolean nextBoolean()
扫描解释为一个布尔值的输入标记并返回该值。
public String next()
查找并返回来自此扫描器的下一个完整标记。
public String nextLine()
此扫描器执行当前行,并返回跳过的输入信息。
public void close()
关闭此扫描器。
注意:
- 录入的数据类型必须和接收类型一一对应
- 录入的数据必须在其取值范围
public class ScannerDemo01 {
public static void main(String[] args) {
//创建Scanner对象
Scanner sc = new Scanner(System.in);
//键盘录入int类型数据
System.out.println("请键盘录入一个整数:");
int num = sc.nextInt();
System.out.println("num = " + num);
//关闭资源
sc.close();
}
}
-
使用Scanner对象完成字符串类型的录入
-
next()和nextLine()的弊端:
next()的弊端:遇到空白符号不再进行扫描
nextLine()的弊端:因为扫描基本数据类型的方法和next()扫描时不识别"回车换行",使用nextLine()的前面不能存在非nextLine()
public class ScannerDemo02 {
public static void main(String[] args) {
//创建Scanner对象
Scanner sc = new Scanner(System.in);
//进行键盘录入
System.out.println("请键盘录入年龄");
int age = sc.nextInt();
System.out.println("age = " + age);
System.out.println("请键盘录入姓名:");
String name = sc.nextLine();
System.out.println("name = " + name);
//关闭资源
sc.close();
}
}
Math类
-
类的特点
数学工具类
-
类的位置
java.lang
-
类的构造器
构造器私有化(方法只能通过类名进行调用)
-
类的方法
public static double random()
返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。
-
生成指定范围内的随机整数小技巧:
(int)(Math.random() * a + b)
a : 范围数据的个数
b : 范围的起始数字
public class MathDemo01 {
public static void main(String[] args) {
//需求:在0-99范围内随机生成整数
System.out.println((int)(Math.random() * 100));
//需求:在1-100范围内随机生成整数
System.out.println((int)(Math.random() * 100 + 1));
//需求:在5-8范围内随机生成整数
System.out.println((int)(Math.random() * 4 + 5));
//需求:在8-15范围内随机生成整数
System.out.println((int)(Math.random() * 8 + 8));
}
}
案例:猜数字小游戏
分析:
1.随机生成一个1-100之间的整数
2.键盘录入一个整数
3.进行数据的比较:
录入数据 > 随机数据
提示: 猜大了
录入数据 < 随机数据
提示: 猜小了
录入数据 = 随机数据
提示: 猜中了
4.考虑到不知道几轮可以猜中数据,选择while的死循环,并在猜中后结束循环
public class MathDemo02 {
public static void main(String[] args) {
method03();
}
public static void method03 () {
//1.随机生成一个1-100之间的整数
int num = (int)(Math.random() * 100 + 1);
//创建Scanner对象
Scanner sc = new Scanner(System.in);
int start = 1;
int end = 100;
//4.考虑到不知道几轮可以猜中数据,选择while的死循环,并在猜中后结束循环
while (true) {
//2.键盘录入一个整数
System.out.println("请输入一个" + start + "-" + end + "范围之间的整数");
int guessNum = sc.nextInt();
//针对键盘录入的数据做健壮性判断
if (guessNum < start || guessNum > end) {
System.out.println("数据输入有误,请重新输入");
continue;
}
//3.进行数据的比较
if (guessNum > num) {
System.out.println("你猜的数字太大了,请重新输入...");
end = guessNum - 1;
} else if (guessNum < num) {
System.out.println("你猜的数字太小了,请重新输入...");
start = guessNum + 1;
} else {
System.out.println("恭喜你,猜中啦,晚上回家可以买彩票去啦~~~");
break;
}
}
//关闭资源
sc.close();
}
public static void method02 () {
//1.随机生成一个1-100之间的整数
int num = (int)(Math.random() * 100 + 1);
//创建Scanner对象
Scanner sc = new Scanner(System.in);
int start = 1;
int end = 100;
//4.考虑到不知道几轮可以猜中数据,选择while的死循环,并在猜中后结束循环
while (true) {
//2.键盘录入一个整数
System.out.println("请输入一个" + start + "-" + end + "范围之间的整数");
int guessNum = sc.nextInt();
//3.进行数据的比较
if (guessNum > num) {
System.out.println("你猜的数字太大了,请重新输入...");
end = guessNum - 1;
} else if (guessNum < num) {
System.out.println("你猜的数字太小了,请重新输入...");
start = guessNum + 1;
} else {
System.out.println("恭喜你,猜中啦,晚上回家可以买彩票去啦~~~");
break;
}
}
//关闭资源
sc.close();
}
public static void method01 () {
//1.随机生成一个1-100之间的整数
int num = (int)(Math.random() * 100 + 1);
//创建Scanner对象
Scanner sc = new Scanner(System.in);
//4.考虑到不知道几轮可以猜中数据,选择while的死循环,并在猜中后结束循环
while (true) {
//2.键盘录入一个整数
System.out.println("请输入一个1-100范围之间的整数");
int guessNum = sc.nextInt();
//3.进行数据的比较
if (guessNum > num) {
System.out.println("你猜的数字太大了,请重新输入...");
} else if (guessNum < num) {
System.out.println("你猜的数字太小了,请重新输入...");
} else {
System.out.println("恭喜你,猜中啦,晚上回家可以买彩票去啦~~~");
break;
}
}
//关闭资源
sc.close();
}
}
Arrays类
-
类的特点
数组的工具类
-
类的位置
java.util
-
类的构造器
构造器私有化
-
类的方法
public static String toString(int[] a)
返回指定数组内容的字符串表示形式。
public static int[] copyOf(int[] original,int newLength)
复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度。
public static void sort(int[] a)
对指定的 int 型数组按数字升序进行排序。
Arrays.sort()的比较规则:
1.基本数据类型数组
针对七种的数据类型数据的大小进行升序排序
布尔类型无法使用Arrays.sort进行排序
2.字符串类型数组
根据Unicode码表进行排序
3.自定义类型对象数组
如果想进行排序,需要给其编写比较规则
比较规则(比较器)
-
自然顺序比较器Comparable< T>
-
定制顺序比较器Comparator< T>
public class ArraysDemo {
public static void main(String[] args) {
//创建基本数据类型数组
int[] arr01 = {44,33,22,11,55};
//创建字符串类型数组
String[] arr02 = {"bca","abc","bac","cba","cab","acb"};
//创建学生类型数组
Student s1 = new Student("崔小荣", 18);
Student s2 = new Student("张小芳", 18);
Student s3 = new Student("安小妮", 18);
Student s4 = new Student("张小弛", 18);
Student s5 = new Student("李小朦", 18);
Student[] arr03 = {s1,s2,s3,s4,s5};
//通过字符串的形式打印数组
System.out.println(Arrays.toString(arr01));
System.out.println(Arrays.toString(arr02));
System.out.println(Arrays.toString(arr03));
System.out.println("======================================");
//通过原来的数组复制指定长度新数组
int[] newArr = Arrays.copyOf(arr01, 10);
System.out.println(Arrays.toString(newArr));
System.out.println("======================================");
//针对数组进行排序
Arrays.sort(arr01);
System.out.println(Arrays.toString(arr01));
Arrays.sort(arr02);
System.out.println(Arrays.toString(arr02));
Arrays.sort(arr03);
System.out.println(Arrays.toString(arr03));
}
}
Student标准类