1.static静态属性
static
是Java中的一个修饰符,可用来修饰成员变量、成员方法
a. 静态变量
被static
修饰的成员变量称为静态变量
- 静态变量被该类的所有成员共享
- 调用方式:
- 类名调用(推荐)
- 对象名调用
例:创建方法
//在创建的类中:
public static String name;
调用:假设类为:Student
//类名调用:
Student.setName("lsh");
Student s1 = new Student();
Student s2 = new Student();
//对象名调用
s1.getName();
s2.getName();
这里的s1和s2所获得的名字都是lsh
存储原理:
在堆内存中有一个静态存储位置(静态区)用来存储用static
修饰的变量
- 静态变量的加载是和类同时进行的,优于对象
- 其他成员变量是在对象创建之后才被加载
当创建一个对象之后,这个对象在堆内存中可以找到静态区和其中的静态变量,随后把地址返回给main方法中的变量。这样就可以通过对象访问静态变量了。
值得注意的是:引用类型的变量在会自动初始化为
null
或0
b.静态方法
静态方法一般在测试类和工具类中使用,在javabean
不常使用
静态方法示例:
public static void main(String[] args)
主要作用就是,把方法封装在一个特定的类中,使用这个类名调用其中的方法
我们来回顾一下不同的类:
-
Javabean类
:用来描述一类事物的类。比如Student,Teacher,Dog,Cat
等 -
测试类
:用来检查其他类是否书写正确,带有main方法的类,是程序的入口 -
工具类
:不是用来描述一类事物的,而是帮我们做一些事情的类。
详细介绍工具类的特点:
- 没有成员变量
- 私有化构造方法:不让外界创建它的对象,没有实际的意义
- 方法定义为静态:可以使用类名调用
- 类名有意义(见名知意)
给出调用的例子:(部分代码)
int[] arr1 = {1, 2, 3, 4, 5};
//调用类中的遍历方法
String str = ArrayUtil.printArr(arr1);//类名调用
System.out.println(str);
double[] arr2 = {1.5, 3.7, 4.9, 5.8, 6.6};
//调用类中的求平均值方法
double avg = ArrayUtil.getAverage(arr2);//类名调用
System.out.println(avg);
案例:(求学生中年龄最大值)
//工具类:
class Math {
private Math() {};
//年龄的最大值
static int Max(ArrayList<Student1> list){
int max = list.get(0).getAge();
for (int i = 1; i < list.size(); i++) {
max = max >= list.get(i).getAge() ? max :list.get(i).getAge();
}
return max;
}
}
//Javabean类
//略(就是一个学生类,包含名字,年龄,性别)
//测试类:
public class b_11_11_2 {
public static void main(String[] args) {
ArrayList<Student1> list = new ArrayList<>();
Student1 s1 = new Student1("lsh", 20, "man");
Student1 s2 = new Student1("ls", 21, "man");
Student1 s3 = new Student1("lh", 23, "women");
list.add(s1);
list.add(s2);
list.add(s3);
int max = Math.Max(list);
System.out.println(max);
}
}
2.static注意事项
- 静态方法中没有
this
关键字 - 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
2.1 静态方法中没有this
关键字
我们前面已经提到,在非静态的方法种实际是有this
关键字的,例如:
//假设我创建了学生类Student,成员变量有name,age
public int Math() {
System.out.println(name + "的年龄是:" + age)
}
当我在main
方法中给对象的成员变量赋值之后,就可以正常输出
是因为:在这个方法中有this
,它的值是调用这个方法的对象的地址
例如:
//在main方法中
s1.Math();
//
public int Math(Student this) {
System.out.println(this.name + "的年龄是:" + this.age)
}
实际上this
就代表了s1
但是在静态方法中没有this
关键字
静态方法可以被对象调用,但是和调用它的对象没有什么关系,所以在设计的时候就没有
this
关键字
2.2 静态方法只能访问静态变量和方法
因为静态方法没有关键字,所以无法访问非静态的变量和方法
例:
//假设我创建了学生类Student,成员变量有name,age
public static void math() {
System.out.println(name);
}
上述代码是会报错的,同样也不能访问非静态的方法
//假设我创建了学生类Student,成员变量有name,age
public static void math() {
System.out.println(name);
show();//假设是我创建的非静态方法
}
当非静态的方法调用其他方法时:
public void math() {
this.show();//假设是我创建的非静态方法
}
实际上是有this.show()
;所以可以访问其他方法
但是在静态方法中没有this
关键字,所以就不能访问
2.3 非静态方法的访问
非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
跟上面的解释一样:
- 因为静态的方法中有
this
关键字 - 同时静态的变量和方法可以被对象调用,所以就可以访问
3. 重新认识main方法
给出下面的代码:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
public
: 被JVM(Java虚拟机)调用,访问权限足够大static
:被JVM调用,不用创建对象,直接类名访问, 因为main方法是静态的,所以测试类中其他方法也需要是静态的void
: 被JVM调用,不需要给JVM返回值main
: 一个通用的名称,虽然不是关键字,但是被JVM识别String[] args
: 以前用于接收键盘录入数据的,现在没用
4. 继承
继承是面向对象的三大特征之一,可以让类和类之间产生子父的关系
为什么使用继承:
- 类和类之间,存在相同的内容并满足子类是父类的一种,可以使用继承来优化代码;例如:学生类和老师类,父类可以为“人”
- 类和类之间没有联系时,不建议使用继承;例如:程序员和手机
继承的格式:
public class 子类 extends 父类 {}
我们同样以学生和老师类为例:
- 学生:成员变量:姓名、性别、年龄;成员方法:吃饭、睡觉、学习、打豆豆
- 老师:成员变量:姓名、性别、年龄;成员方法:吃饭、睡觉、教书
在这两个类中有很多重复的部分,例如:姓名、性别、年龄;行为:吃饭、睡觉
为了优化代码,我们可以把上述的内容封装在另一个类中,比如:person
,作为父类,子类可以继承这些内容,并且子类可以有自己独特的成员变量和方法
这就是继承
4.1 继承的特点
Java只支持单继承,不支持多继承,但支持多层继承
- 单继承:一个子类只能继承一个父类
- 多层继承:子类A继承父类B,父类B可以继承父类C
在Java中没有爷爷等的说法,以上面为例:父类B称为A的直接父类,父类C称为A的间接父类
Java支持多层继承,所以在Java设计的时候,有一个最大的父类object
,所有的类都继承于这个类。
例如:
public class c_11_12 {
public static void main(String[] args) {
student s1 = new student();
s1.equals('a');
}
}
class student{}
如上图所示,在student
中我并没有写任何的方法,但是仍然可以调用一些方法,这就是继承于object
的方法
示例来自黑马程序员
具体代码示例:
写出以下类:并使用继承优化代码
- 狸花猫:吃饭、喝水、抓老鼠、有一定危险
- 布偶:吃饭、喝水、抓老鼠、可爱
- 哈士奇:吃饭、喝水、看家、拆家
public static void main(String[] args) {
Ragdoll ra = new Ragdoll();
ra.Cue();
ra.CatchMouse();
ra.Drink();
ra.Eat();
System.out.println("------------");
Hasky ha = new Hasky();
ha.BreakHome();
} //测试类
class Animal{
public void Drink(){
System.out.println("喝水");
}
public void Eat(){
System.out.println("吃饭");
}
} //动物类
class Cat extends Animal{
public void CatchMouse(){
System.out.println("抓老鼠");
}
}//猫类
class Dog extends Animal{
public void Lookhome() {
System.out.println("看家");
}
}//狗类
class lihua extends Cat{
public void Danger() {
System.out.println("危险");
}
}//狸花猫
class Ragdoll extends Cat{
public void Cue() {
System.out.println("可爱");
}
}//布偶
class Hasky extends Dog{
public void BreakHome() {
System.out.println("拆家");
}
}//哈士奇
- 当我们写代码的时候,可以采用先画结构图,再写的方法
- 写的时候先从最高的父类开始写,依次往下
需要我们注意的是:
当父类中的方法为私有private
时,子类不能继承
4.2 父类中成员私有和非私有
父类中的哪些成员是子类可以继承的呢:
- 构造方法:不管是不是非私有,都不可以继承
- 成员变量:都可以继承,但是父类中私有的成员变量不能直接调用
- 成员方法:非私有可以继承,但是私有不可以继承
a.构造方法
这个很好理解,如果可以继承父类的构造方法,就不符合Java的语法规范了
- 因为父类的名字和子类不一样,但是构造方法要求必须和类名一致
- 如果可以继承就会报错
b.成员变量
不管是不是私有的成员变量,在创建子类的对象的时候,都会在堆内存中开辟一个包含父类中成员变量的方法。
在访问的时候会先访问子类中已有的成员变量,之后再访问从父类中继承的成员变量。
值得注意的是:虽然父类中私有的成员变量可以继承,但是不能直接访问
我们来看内存图:来自黑马程序员
可以看到,上面父类的成员变量是再子类的对象中的:
private String name;
private int age;
//子类中的
String game;
但是不能直接进行访问和修改,如果要访问要用到父类中的getter和setter
方法
同时,我们可以看到,在方法区中,当加载子类的时候,同时也加载了父类。当然
object
作为最大的父类,也是会被加载
c.成员方法
前面说到:非私有的方法可以被继承,其实准确的来说:
虚方法表中的方法可以被继承
而非私有的成员方法在虚方法表中
虚方法表:
- 非
private
方法 - 非
static
方法 - 非
final
方法
在继承的时候虚方法表中的方法会根据子类而改变:
在虚方法表中会添加上子类自己的虚方法,这样在调用父类中的方法时,就可以直接在虚方法表中进行查找,而不需要一层一层向父类查找
- 在虚方法表外的方法还是要一层一层的向父类查找,效率较低
object
中有5个方法在虚方法表中
4.3 继承中的就近原则
当局部变量、成员变量、父类中的成员变量重名了,我们在调用的时候该使用哪一个呢?
看代码:
public class Fu {
String name = "Fu";
}//父类
public class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name = "ziShow";
System.out.println(name);
}
}//子类
其遵循“就近原则”:
- 先访问离自己最近的局部变量
- 如果没有局部变量,就访问本类中的成员变量
- 如果子类中没有这个名字的成员变量,就访问父类中的成员变量
- 如果都没有就报错
同时前面我们介绍了this
关键字,这里再介绍super
关键字
super
关键字使用方法和this
相同,但是super
指的是访问当前类的父类中的成员变量
例:在上面的代码中:
System.out.println(super.name);
输出的结果就为FU
super
只能调用最近的父类,不能调用多个- 同样使用
this.name
,结果就为:ZI
同样的在父类和子类中有重名的方法的时候,也和上面的一样:
- 就近原则:谁离得更近就调用谁
this
:调用本类的成员方法super
:调用最近父类的成员方法
4.4 方法重写
当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式:
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法,是重写的方法。
@Override
重写注解
-
@Override
是放在重写后的方法上,校验子类重写时语法是否正确。 -
加上注解后如果有红色波浪线,表示语法错误。
-
建议重写方法都加@Override注解,代码安全,优雅
示例:
class FU{
public void Live() {
System.out.println("正在努力的生存");
}
}
class ZI{
@Override
public void Live() {
System.out.println("傻傻的享受");
}
}
更进一步来说,实际上方法的重写是对虚方法表中的方法进行了覆盖
例如:三个类:A、B、C,A是B的子类,B是C的子类
当在B中重写从C继承过来的方法(假设为方法M),在B的虚方法表中就把方法M改为了B中的。如果A也进行M的重写,在A的虚方法表中方法M就变为了A中的。
图例:
方法重写注意事项和要求:
-
重写方法的名称、形参列表必须与父类中的一致。
-
子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解:
空着不写 < protected < public
) -
子类重写父类方法时,返回值类型子类必须小于等于父类
-
建议: 重写的方法尽量和父类保持一致。
-
只有被添加到虚方法表中的方法才能被重写
4.5 父类构造方法的调用
子类不会继承父类的构造方法,但是子类中的所有构造方法会默认先访问父类中的无参构造,再执行自己
为什么:
-
子类在初始化的时候,有可能会使用到父类中的数据。如果父类没有完成初始化,子类将无法使用父类的数据。
-
子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
示例:
class Zi{
//子类中空参构造
public Zi() {
super();//即使不写也会有
}
//有参构造
public Zi(String name) {
super(name);//当name是继承自父类的时候需要手动加上
}
}
}
注意事项:
-
子类不能继承父类的构造方法,但是可以通过super调用
-
任何子类构造方法的第一行,有一个默认的super(),这是为了确保在实例化子类对象时,父类也能被正确的初始化
-
默认先访问父类中无参的构造方法,再执行自己。
-
如果想要方法文父类有参构造,必须手动书写。
this
和super
的总结:
-
this: 理解为一个局部变量,表示当前方法调用者的地址值;
-
super: 代表父类存储空间。
- this 在访问构造方法的时候,访问的是本类的其他构造方法
- 使用场景一般为:在无参构造是设置默认的初始值;
public class Student {
String name;
int age;
String school;
public Student () {
//表示调用本类其他构造方法
//细节:虚拟机就不会再添加super();
this(name: null,age:0,school:"传智大学");
}
public Student(String name, int age, String school) {
//super();
this.name = name;
this.age = age;
this. school = school;
}
}
在方法中使用this调用了其他的本类构造方法时,JVM(Java虚拟机)就不会在这个类中添加
super()
了,因为其他类中一定会有显式或者隐式的super()
标签:调用,Java,进阶,子类,面向对象,父类,方法,public,变量 From: https://blog.csdn.net/2302_80203877/article/details/143671534