课前小练习
public class Demo1 {
public static void main(String[] args) {
Person p1= new Person();
Person p2= new Person();
p1.compare(p2);
}
}
class Person{
private String name ;
private int age;
public void setName(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
//封装方法,比较年龄是否相同
public boolean compare(Person p){
return this.age == p.age;
}
一. 静态
(一)静态概述
- 静态就是 static , 主要用来修饰java的变量和方法的关键字。
- 没有静态与有静态的场景对比:
a : 没有静态
如果某个类型的所有对象,都具有一个相同的属性值,那么这个属性值就没有必要在所有对象中,都存储一份。还有坏处:浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象
b: 有静态
如果某个类型的所有对象,都具有一个相同的属性值,那么就在这个属性的定义上,加一个static静态关键字。让该变量存储在方法区字节码的静态区中,避免了所有对象都存储相同数据的问题,节省了内存空间,将来维护容易(只需要修改一次)
(二) 静态的内存理解
- 没有静态内存图:
- 有静态的内存图:
(三) 静态变量的特点
- 静态变量属于类, 不属于对象
- 加载时机:
随着类的加载而加载
静态变量随着类的加载进方法区,就直接在静态区给开辟了存储静态变量的内存空间 - 静态变量优先于对象而存在
- 静态变量被所有该类对象所共享
- 调用方式:
类名调用(常用) 或者 创建对象调用
(四) 静态访问的注意事项
1、静态方法:在方法声明上,加上了static关键字的方法,就是静态方法
2、静态方法不能访问非静态的变量
原因:
静态方法可以在没有创建对象的时候调用,而非静态的变量只有在对象创建之后才存在。如果静态方法可以访问非静态的变量,那么就相当于在对象创建之前,就访问了对象创建之后的数据。明显不合理。
3、静态方法不能访问非静态的方法
原因:
静态方法可以在没有创建对象的时候调用;非静态的方法可以访问非静态的变量。如果静态方法可以访问非静态的方法,就相当于静态方法间接的访问了非静态的变量,和第2点矛盾。
4、静态方法中不能存在this关键字
原因:
this关键字表示本类当前对象。静态方法可以在对象创建之前调用。如果静态方法可以访问this关键 字,相当于在创建对象之前,就使用了对象本身,矛盾
(五) 静态成员变量和非静态成员变量的区别
- 概念上,所属不同:
非静态变量属于对象
静态变量属于类,类变量 - 内存空间不同,存储位置不同
非静态变量属于对象,所以存储在堆内存中
静态变量属于类,存储在方法区的静态区中 - 内存时间不同,生命周期不同
非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失
静态变量属于类,所以生命周期和类相同,随着类的加载而存在,随着类的消失(内存管理)而消失 - 访问方式不同
非静态变量只能使用对象名访问
静态变量既可以使用对象访问,也可以通过类名访问:
类名.静态变量名 或者 对象名.静态变量
类名.静态方法名()
import javax.naming.Name;
public class Demo2 {
public static void main(String[] args) {
/* Student s1 = new Student();
s1.name= "小明";
s1.age = 20;
s1.country = "中国";
Student s2 = new Student();
s1.name= "小丽";
s1.age = 18;
s1.country = "中国";*/
Student s1 = new Student();
s1.name= "小明";
s1.age = 20;
System.out.println(s1.country);
Student s2 = new Student();
s1.name= "小丽";
s1.age = 18;
System.out.println(s2.country);
System.out.println(Student.country); //推荐方式
// s1.method();
// Student.method(); //推荐方法
System.out.println(Student.num);
}
}
/*
* static:
* 静态的意思,是一个成员修饰符,修饰成员变量或方法。
* 没有静态前:
* 学生类型的所有对象都有一个相同的属性值,即国家的属性值:中国。
* 在堆中存储每个对象时,每块空间中有一个“中国”,占用内存空间,维护成本也高,一旦值发生改变,每块空间中的“中国”都要修改。
* 使用静态修饰符:
* 学生类中的country被static修饰符后,存储在方法区,student.class文件中的静态区域中
* 该属性值会被student类型的所有对象所共享,在内存中只存储一份,节约了内存空间,并降低了维护成本。
*
* 修饰变量:
* 1.随着类的加载而存储,随着类的消失而消失,生命周期长。
* 2.叫做类变量、静态变量,通过类名可以直接访问(推荐方式)。
* 3.优先于对象存储在内存中,通过对象名.静态变量名也可以访问(不推荐的方式)
* 4.可以被所有对象所共享
* 注意: 必须是某类型的所有对象的共享资源数据时,可以用static修饰
*
* 修饰方法(静态方法或类方法):
* 1.可以使用类名或对象名去调用,类名调用是推荐方式
* 2.静态方法只能直接访问静态成员,不可以直接访问非静态成员
* 想要访问非静态成员,必须在静态方法中创建对象,通过对象去访问。
* 3.在静态方法中,不能出现this或super关键字。
*
* 注意:
* 1.非静态方法中既可以访问静态成员也可以访问非静态成员
* 2.该类中没有需要维护的数据时(即没有属性),方法都可以是静态的,即通过类名去访问,常见用于是工具类。
*
* 数组:
* 1.遍历 2,获取最值 3.反转 4、排序...
* 步骤: 1.构造方法私有化 2.方法静态
* */
class Student{
String name;
int age;
static String country = "中国";
static int num;
public static void method(){
Student s = new Student();
System.out.println(s.name);
System.out.println(country);
s.show();
// System.out.println(this);
}
public void show(){
System.out.println(country);
method();
}
}
public class ArrayTools {
//1.构造方法私有化
private ArrayTools(){}
//2.方法都是静态的
/*
* 获取数组中最大元素
* */
public static int getMax(int[] arr){
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max < arr[i])
max = arr[i];
}
return max;
}
/*
* 获取数组中最小元素
* */
public static int getMin(int[] arr){
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (min > arr[i])
min = arr[i];
}
return min;
}
/*
* 反转数组中元素
* */
public static void reverse(int[] arr){
for (int i = 0; i < arr.length/2; i++){
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
}
}
import java.util.Arrays;
public class Demo3 {
public static void main(String[] args) {
// int[] arr = {3,1,4,8,5};
// ArrayTools.getMax(arr);
// Arrays.sort(arr);
// System.out.println(args);//[Ljava.lang.String;@1540e19d
// System.out.println(args.length); //0
//
// System.out.println(args[0]);
String[] arr = {"a","b","c"};
Demo.main(arr);
}
}
class Demo{
public static void main(String[] args) {
System.out.println("执行了");
System.out.println(args.length+"...");
}
}
二. 继承
(一) 继承的概述
-
继承概述: 继承是面向对象三大特征之一,让类与类产生子父类的关系.可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法一种体现方式
-
继承的实现:
使用关键字extends,表示: 扩展、增加、继承 -
格式:
class 子类 extends 父类 { } 父类:被继承的类,超类、基类 子类:用于继承的类,派生类 举例: class Dog extends Animal { }
public class Demo4 {
public static void main(String[] args) {
Student1 s = new Student1();
s.name = "小刚";
//s.age = 18;
s.setAge(18);
System.out.println(s.getAge());
s.sId = 10001;
s.eat();
s.study();
}
}
class Person1{
String name;
private int age;
public Person1(){}
void eat(){
System.out.println("eat");
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
class Student1 extends Person1{
int sId;
void study(){
System.out.println("study java"+name+"..."+getAge());
}
/* public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}*/
}
(二) 继承的好处
-
继承的好处:
(1) 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
(2) 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
(3) 为多态提供了前提 -
继承中弊端:
继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性 -
应用场景:
使用继承,需要考虑类与类之间是否存在is…a的关系,不能盲目使用继承
is…a 的关系:谁是谁的一种
例如:老师和学生都是人的一种,那人就是父类,学生和老师就是子类
(三) 继承的注意事项
1、私有的成员不能被继承
父类中有一些私有成员,不能在子类中直接使用
其实在子类对象中,仍然包含了父类中定义的私有成员变量
只不过在子类中,不能直接访问父类中定义的私有成员变量
2、父类中的构造方法,不能继承
原因:
父类的构造方法需要和父类的类名一致、子类的构造方法需要和子类类名一致,父 类和子类的类名不一样。因此无法继承,名称有冲突。
父类的构造方法用于给父类的成员变量赋值,子类的构造方法用于给子类的成员变 量赋值,子类的成员变量较多,使用父类的构造方法无法将子类中所有的成员变量都进 行赋值,因此不继承父类的构造方法
(四) 继承的特点
1、java支持单继承,不支持多继承,支持多层继承
单继承:一个子类只能继承一个父类
不能多继承:一个子类不能同时继承多个父类
可以多层继承:A类可以继承B类,B类可以继承C类,A类中拥有B、C类中的所有属性和方法。说明:越是顶层的类,定义的功能越是共性功能,功能和属性就越少;越是底层的类,定义的特有功能和属性就越多,就更加强大。学习一个体系的时候,先学顶层的类,共性功能学习完,学习底层特有的方法即可;使用一个类创建对象的时候,选择底层的类型,功能更多更强大。
2、原因:
如果支持多继承,那么可能一个子类继承了两个父类,两个父类中有相同的方法声明,却拥有不同的方法实现。子类继承之后,就不知道该走哪个父类的方法实现了(安全隐患)。
(五) 继承中成员变量的关系
-
父类中定义了成员变量,子类中没有定义,那么子类可以直接使用父类中非私有成员
-
父类中没有定义成员变量,子类中定义了,子类可以自己调用自己的变量,父类不能调用子类特有变量
总结: 子类可以使用自己和父类的成员变量,父类只能使用自己的成员变量 -
父类中定义了变量,子类中重新定义了这个变量,在子类中调用的就是子类的变量
原因: 变量的访问就近原则 1) 方法内部,自己定义了变量,使用方法内部变量 2) 方法内部没有定义,找当前类中成员变量 3) 类中成员变量也没有定义,找父类中的成员变量 4) 父类中没有定义变量,再继续找父类的父类,直到找到最后(Object类,所有类的直接或者间接的父类,Object是一个最顶层的类,也没有找到,才报错
-
如果子父类中成员变量重复定义, 想调用父类成员变量, 可以使用关键字 super
使用 : super.父类成员变量名;
(六) 继承中成员方法的关系
- 父类中私有方法不能被子类继承使用
- 子类中定义的和父类中继承到的方法不同, 子类可以调用自己特有方法功能, 可以调用从父类继承来的功能
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容, 是对父类方法的功能进行了拓展和维护
- 重写Override注意事项:
- 重写需要与父类原方法, 返回值类型, 方法名 ,参数列表相同
- 私有方法不能被重写
- 子类方法访问权限不能更低(public > 默认 > 私有)
- 在子类重写方法上, 通常使用注解 @Override, 用来检测当前的方法, 是否是重写的方法,起到【校验】的作用
注意 : 重写的方法本身还是属于父类,只是在子类中重新实现了这个方法的内容。
(七) super关键字
- super :建立在继承关系中用来使用父类资源的关键字
- 使用场景: 子父类中的成员变量或方法重名时, 可以使用super关键字表示父类成员调用
super只能访问父类中定义的成员变量
语法: super.成员变量名;
super只能访问父类中定义的成员方法
语法: super.成员方法名(实际参数);
(八) 关键字this和super的详细解释
1、this和super:
this表示本类当前对象的引用
super表示本类当前对象父类的引用
2、图示:
(九) 继承中构造方法的关系
- 父类中构造无法被子类继承, 但是子类构造方法中可以调用父类构造; 在子父类继承关系中, 父类优先于子类进入到内存, 父类中数据优先于子类进行初始化
- 如果子类构造方法中, 没有手动调用任何构造(本类, 父类),系统会默认在子类构造方法第一行调用super(); 目的是为了让父类中成员优先于子类进入内存中赋值
- 如果子类构造方法中, 手动调用本类或者父类构造, 那么系统不会再默认调用任何构造, 一律以手动调用构造为准
- super() : 父类空参构造调用. 必须写在构造方法第一位置上, 直接保证父类构造优先于子类进入内存运行
public class Demo1 {
public static void main(String[] args) {
//出现了继承,加载类时,先加载父类,再加载子类
Zi zi = new Zi("",10);
}
}
/*
* 继承出现后:
* 构造方法变化:
* 构造方法的作用:主要用于给对象的属性进行初始化。
* 创建子类对象,发现父类的构造方法也执行了,意味着让父类的属性先初始化,然后子类属性在初始化。
* 在子类的构造方法的第一行有一条隐藏的 super(); 语句:这个语句就是去调用父类的无参数构造方法
* 如果父类中没有无参数构造方法,那么子类构造方法的第一行必须显示写出 super(参数); 语句.
*
* 父类的构造方法执行了,那么父类的对象创建了吗?
* 没有创建。仅仅是子类中持有一个父类型引用,即super,super可以调用父类中的所有成员,变量、方法、构造方法。
*
* 子类构造方法:
* 重载构造方法调用 : this(参数); 需要在第一行
* 调用父类的构造方法: super(参数); 需要在第一行
* this() 和 super() 是不能共存的,在一个构造方法中,如果写了this()就不能写super() 写了super(),就不能写this()。
*
* */
class Fu{
//构造方法
public Fu(){
System.out.println("父");
}
public Fu(int x){
System.out.println("父---"+x);
}
}
class Zi extends Fu{
private String s;
private int a;
//构造方法
public Zi(){
super();
System.out.println("子");
}
public Zi(String s){
this();
this.s = s;
//System.out.println("子---"+s);
}
public Zi(String s,int a){
this(s);
this.a = a;
//System.out.println("子---"+s+"..."+a);
}
}
(十) this和super使用总结
1、含义:
this关键字表示本类当前对象的引用
哪个对象在调用this所在的方法,this就表示哪个对象
super关键字表示本类当前对象的父类引用
哪个对象在调用super所在的方法,super就表示哪个对象中的父类部分的数据
2、super和this都可以访问成员变量
super只能访问父类中定义的成员变量
super.成员变量名
this既可以访问子类中定义的成员变量,也可以访问父类中定义的成员变量
this.成员变量名
3、super和this都可以访问成员方法
super只能访问父类中定义的成员方法
super.成员方法名()
this不仅可以访问子类中定义的成员方法,也可以访问父类中定义的成员方法
this.成员方法名()
4、super和this都可以访问构造方法:this语句和super语句
this():访问本类的其他构造方法
super():访问父类的构造方法
public class Demo5 {
public static void main(String[] args) {
Zi zi = new Zi();
//System.out.println(zi.num);
//zi.show();
//zi.method();
zi.study();
}
}
class A{}
class B extends A{}
class Fu{
int num = 10;
public void method(){
System.out.println("fu");
}
public A study(){
System.out.println("走路");
return null;
}
}
class Zi extends Fu{
//int num = 100;
public void show(){
int num = 1000;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
public void method(){
System.out.println("zi");
}
@Override
public B study(){
System.out.println("开车");
super.study();
return null;
}
}
/*
* 继承出现后:
* 类中成员的变化:
* 变量:
* 子父类中出现相同的变量,创建子类对象,访问该变量,默认访问是子类中的。
* super:
* 超级的意思,代表父类的引用。用法与this很相似。
* this:代表的是当前类对象的引用,可以访问当前类中的成员,也可以访问父类中的成员
* super:代表的是父类对象的引用,只能访问父类中的成员。
* 方法:
* 子父类中出现相同的方法定义,方法体不同,创建子类对象,调用该方法,默认运行的是子类中的方法,
* 这种现象叫做方法的重写/覆盖(override)。
* overload:重载,方法名相同,参数列表不同
* override:重写,子类重复父类中继承的方法。
* 为什么重写?
* 继承的方法的功能不满于子类的需求。
* 重写方法需要注意?
* 1.权限修饰符要大于等于父类。
* public protected 默认 private
* 2.方法名和参数列表要与父类的方法相同
* 3.返回值类型可以小于等于父类的 jdk5.0版本
* 通常:修饰符 返回值类型 方法名 参数列表 与父类的相同
* 验证是否是重写方法: 在重写的方法的上边写一个注解 @Override