一、static
1.关于JavaBean类中的成员变量
public class Student {
private String name;
private int age;
private String gender;
//新增老师姓名
public static String teacherName;
}
==public static String teacherName;==表示Student类中的所有对象都共享teacherName。
关于调用:
Student.teacherName = "王一博";
2.概述
static表示静态,时Java中的一个修饰符,可以修饰成员方法,成员变量
(1)静态变量
被static修饰的成员变量,叫做静态变量
特点:被该类的所有对象共享
调用方式:类名调用(推荐),对象名调用
<1>static内存图
String name;
int age;
String gender;
//新增老师姓名
public static String teacherName;
public void show(){
System.out.println(name + ", " + age + ", " + gender
+ ", " + teacherName);
}
public class studentTest {
public static void main(String[] args) {
Student.teacherName = "肖战";
Student s1 = new Student();
s1.name = "王一博";
s1.age = 23;
s1.show();
Student s2 = new Student();
s2.show();
}
}
分析:
1)main()进栈, Student.teacherName = “肖战”;把Student类的字节码文件(.class文件)加载到方法区,然后堆内存中单独创建一个静态存储位置(静态区)(JDK8以后),默认初始化值null(目前再内存中没有对象—>静态变量时随着类的加载而加载的,优先于对象的出现);
2)Student s1 = new Student();栈内存定义s1,在堆内存中开辟空间,存储所有的非静态变量,返回地址值赋给s1;
3)s1.show();show方法进栈,通过s1,找到堆内存里的name,age,再去静态区找到teacherName;
4)创建s2(在堆内存里另开辟一段空间),通过s2,也可以找到静态区里的teacherName。
(2)静态方法
被static修饰的成员方法,叫做静态方法
特点:多用在测试类和工具类中,在JavaBean类中很少会用
调用方式:类名调用(推荐),对象名调用
<1>三种类
1)JavaBean类:用来描述一类事物的类,如:Student,Teacher等
2)测试类:用来检查其他的类是否书写正确,带有main方法的类,是程序的入口
3)工具类:帮助我们做一些事情,但不描述任何事物的类
<2>工具类遵循规则
1)见名知意
2)私有化构造方法
目的:不让外界创建它的对象
public class ArrUtil{
private ArrUtil(){}
}
使用:定义学生工具类,获取集合中最大学生年龄
public class StudentUtil {
private StudentUtil() {
}
//静态方法
public static int getStudentAgeMax(ArrayList<Student> list) {
//定义索引参照物
int max = 0;
//遍历集合
for (int i = 1; i < list.size(); i++) {
int tempAge = list.get(i).getAge();
if (list.get(max).getAge() < tempAge) {
max = i;
}
}
return max;
}
}
调用工具类:
//调用工具类中的方法
int max = StudentUtil.getStudentAgeMax(list);
在工具类中的方法里,几个注意点:
—>使用索引参照,也可以直接默认集合中的第一个元素为初始最大值
—>使用tempAge,提高代码的复用性
—>循环从1开始,尽可能减少循环次数
3)方法都定义为静态
3.static的注意事项
(1)静态方法只能访问静态变量和静态方法
(2)非静态方法可以访问静态变量或静态方法,也可以访问非静态的成员变量和非静态的成员方法
(3)静态方法中没有this关键字
分析:
<1>代码角度
public class Student {
String name;
int age;
//新增老师姓名
public static String teacherName;
public void show(){
System.out.println(name + ", " + age + ", " + teacherName);
}
public static void methond(){
System.out.println("静态方法");
}
public class studentTest {
public static void main(String[] args) {
Student.teacherName = "王一博";
Student s1 = new Student();
s1.name = "王二博";
s1.age = 23;
s1.show();
Student s2 = new Student();
s1.name = "王三博";
s1.age = 22;
s2.show();
}
}
—>非静态方法中有this关键字,public void show(Student this)。在调用该方法时,不是手动赋值,由虚拟机进行赋值,this表示的是当前调用者的地址值第一次s1调用,把s1赋值给this,第二次调用,把s2赋值给this,起到区分成员变量的作用(this.name中的this省略不写)。在静态方法中没有this
非静态一般与对象有关,静态方法一般都是共享的,与某一对象没关系
—>静态不能访问非静态(成员变量,成员方法),成员变量和成员方法与对象相关
—>非静态访问所有,静态访问是可以用对象访问的,所以与对象相关的非静态是可以访问静态的
<2>内存角度
—>静态数据与非静态内存加载到内存的时机是不一样的,静态是随着类的加载而加载的,非静态是和对象有关的。当静态数据加载到内存中,而没有创建对象,此时是没有非静态的数据。静态之间是可以调用的,静态无法调用非静态。
public class studentTest {
public static void main(String[] args) {
Student.teacherName = "王一博";
Student.method();
}
}
public class Student {
String name;
int age;
//新增老师姓名
public static String teacherName;
public void show(){
show();//实际上报错
}
public static void method(){
System.out.println(name + ", " + teacherName);//实际这里会报错
}
}
分析:
—>main方法进栈,Student.teacherName = “王一博”;Student类字节码文件加载到方法区,jdk7以后,静态区中存储静态变量。Student.method();找到方法区中的method()字节码文件,加载到栈内存中,获取两个变量name和teacherName,去静态区找这两个变量。而在静态区没有name(没有创建对象)。因为静态方法不能调用非静态变量
—>show()进栈,没有调用show的对象。
4.main方法
public:被JVM调用,访问权限足够大
static:被JVM调用,不用创建对象,直接类名访问。因为main方法是静态的,所以测试类中其他方法也需要是静态的
void:被JVM调用,不需要给JVM返回值
main:一个通用名称,不是关键字,但能被JVM识别
String[] args:以前用于接收键盘录入数据,现在没用。不是在控制台接收数据,在Run/Debug Configurations中Test里的Build and run里的数据
二、继承
封装—>对象
当JavaBean类越来越多,重复的成员变量,成员方法越来越多
例如:两个类Student和Teacher,他们都有成员变量name,age等,可以把这些重复的成员变量拿出来,封装成一个新的类Person
1.概述
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
public class Student extends Person {};
Student成为子类(派生类),Person成为父类(基类或超类)
好处
1)可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
2)子类可以在父类的基础上增加其他功能,使子类更加强大
什么时候用到继承
当类与类之间,存在相同的内容,并满足子类是父类的一种,就可以考虑使用继承
2.特点
Java只支持单继承,不支持多继承,但支持多层继承
(1)单继承:一个子类只能继承一个父类
(2)多继承:一个子类不能继承多个父类
(3)多层继承:子类A可以继承父类B,父类B可以继承父类C(祖孙关系),C是A的间接父类
每一个类都直接或间接的继承于Object
分析共性和异性
画图:从下往上,下面是子类,上面是父类,把共性内容抽取到父类中
代码:从上往下
(4)子类只能继承父类中非私有的成员
3.子类到底能继承父类中的哪些内容
(1)两个误区
1)父类私有的东西,子类就无法继承
2)父类中非私有的成员,就被子类继承下来了
(2)内存图
能不能被子类继承(能继承,不一定能调用)
构造方法 |
非私有 (不能) |
private(不能) |
成员变量 |
非私有(能) |
private(能) |
成员方法 |
非私有(能) |
private(不能) |
所有成员变量都能继承下来,但不一定能用
1)构造方法
假设父类的构造方法能被继承,继承过去后,发现构造方法的名字与类的名字不一致,违背了构造方法名与类名一致的原则
2)成员变量
父类的private成员变量不能直接被子类使用
子类的对象在给变量赋值时,先查找堆内存中子类的地址空间,当找不到对应的成员变量时,再查找父类的。
public class Fu{
private String name;
private int age;
}
public class Zi extends FU{
String game;
public class Demo03 {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z);
z.name = "王一博";//实际上报错
z.age = 23;//实际上报错
z.game = "王者荣耀";
}
}
分析:
<1>Zi z = new Zi();在堆内存中开辟空间并以分为二,分别存储父类成员变量和子类成员变量;返回地址值给z
<2>z.name = “王一博”;在堆内存找到z地址,先在子类成员变量中查找,是否有name。没找到,再从父类成员变量中找,发现name是一个私有变量,赋值失败。age同理
3)成员方法
A类—>B类—>C类
A怎么调用C的成员方法?
在顶级父类C里设计了一个虚方法表,把可能经常要用的方法,放在里面
虚方法表(满足:非private,非static,非final),C把虚方法表交给自己的子类B,B也有自己的虚方法表,它在C的虚方法表基础上加上自己类中的虚方法,A再继承B,同理A也有虚方法表,再B类基础上加上自己的。
A类先检查是不是虚方法,如果是,直接调用,如果不是,检查自己的成员方法,找到调用,否则回去父类的成员方法,确定是否为私有方法。
4.继承中的成员变量
特点
就近原则
public class Fu {
String name = "Fu";
}
public class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name = "ziShow";
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
1)name—>局部位置找到name,输出 ziShow
2)this.name—>本类成员位置的name,既String name = “Zi”;故输出 Zi
3)super.name—>super表示父类,String name = “Fu”;输出 Fu
在子类中只能调用一次super
5.继承中的成员方法
(1)特点
直接调用满足就近原则
super调用,直接访问父类
(2)方法的重写
当父类的方法不能满足子类现在的需求,需要进行方法重写
1)书写格式:
在继承体系中,子类出现了和父类中一模一样的方法声明,就称子类这个
方法是重写的方法。
2)@override重写注解
<1>@override是放在重写后的方法上,校验子类重写时语法是否正确。
(3)方法重写的本质
在虚方法表进行添加时,在子类中发生了方法重写,子类中的方法会父类里面继承过来的方法覆盖
(4)方法重写注意事项和要求
1)重写方法的名称、形参列表必须与父类一致
2)子类重写父类方法时,访问权限子类必须大于等于父类
3)子类重写父类方法时,返回值类型子类必须小于等于父类
4)建议:重写方法尽量和父类保持一致
5)私有方法不能被重写
6)只有被添加到虚方法表里的方法才能被重写
6.继承中的构造方法
(1)特点
1)父类中的构造方法不会被子类继承
2)子类中所有构造方法默认先访问父类中的无参构造,再执行自己
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类数据
子类在初始化之前,一定要先调用父类构造方法先完成父类数据空间的初始化
(2)怎么调用父类构造方法
1)子类构造方法的第一行语句默认都是:super()(调用父类的无参构造),不写也存在,且必须在第一行
2)如果想要调用父类的有参构造,必须手动写super进行调用
eg:super(name,age);直接传递变量
7.this,super总结
this:理解为一个变量,表示当前方法调用着的地址值
super:代表父类存储空间
关于使用
this(…) – 调用本类的其他构造方法
eg:需求:默认对象是王一博
可以在空参构造里写:
public Studnt(){//空参构造
this(null,0,"王一博");//如果有其他有效语句,必须写在this()的下面
}//细节:虚拟机不会添加super()
8.继承父类后
public class Buyer extends AdminStaff{
public Buyer() {
}
public Buyer(String name, String id) {
super(name, id);
}
@Override
public void work(){
System.out.println("采购专员正在采购!");
}
}
(1)子类私有成员变量
(2)空参构造方法
(3)带参构造方法(super)
(4)方法重写