1、Static:
static是什么?
- 它是静态的意思,可以修饰成员变量和成员方法。
- static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。
static修饰成员变量的用法:
成员变量可以分为两类:
- 静态成员变量(有static修饰的,属于类,内存中加载一次):经常表示如在线人数信息等需要被共享的信息,可以被共享访问。
- 访问:类名.静态成员变量(推荐) / 或 对象.静态成员变量(不推荐)
- 实例成员变量(无static修饰,存在于每个对象里):常表示名字name、年龄age、等属于每个对象的信息。
- 访问:对象.实例成员变量
使用user用户类进行代码演示:
public class User { /** 在线人数。 注意:static修饰的成员变量:静态成员变量,只在内存中有一份,可以被共享 */ public static int onlineNumber = 161; /** 实例成员变量:无static修饰,属于每个对象的,必须用对象名,访问 */ private String name; private int age; public static void main(String[] args) { //目标: 理解static修饰成员变量的作用和访问特点。 //1、类名.静态成员变量。(有static修饰,属于类,内存中加载一次):常表示如在线人数信息等 //需要被共享的信息,可以被共享访问。 System.out.println(User.onlineNumber);//161 //2、对象名.实例成员变量。(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。 //System.out.println(User.name); // 报错 User u = new User(); u.name = "张三"; u.age = 21; System.out.println(u.name);//张三 System.out.println(u.age);//21 u.onlineNumber++ ; // 新来了一个人 System.out.println(u.onlineNumber);//162 User u2 = new User(); u2.name = "张三"; u2.age = 21; System.out.println(u2.name);//张三 System.out.println(u2.age);//21 u.onlineNumber++ ; // 新来了一个人 System.out.println(u.onlineNumber);//163 System.out.println(User.onlineNumber); //推荐方式//163 //注意:同一个类中静态成员变量的访问可以省略类名。 System.out.println(onlineNumber);//163 } }
static修饰成员方法的基本用法:
成员方法的分类:
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,归属于对象),只能用对象触发访问。
使用学生类的代码演示:
public class Student { /** 实例成员变量:无static修饰,属于对象 */ private String name; /** 静态成员方法:有static修饰,归属于类,可以被共享访问,用类名或者对象名都可以访问 */ public static int getMax(int age1 , int age2){ return age1 > age2 ? age1 : age2; } /** 实例方法:属于对象的,只能用对象触发访问 */ public void study(){ System.out.println(name + "在好好学习,天天向上-"); } public static void main(String[] args){ //1、类名.静态成员方法 System.out.println(Student.getMax(10, 3));//10 //注意:同一个类中,访问静态方法,类名可以省略不写。 System.out.println(getMax(10, 32));//32 //study(); //报错了,一定要拿对象访问。 //2、对象.实例方法 Student s = new Student(); s.name = "猪八戒"; s.study(); //3、对象.静态方法(语法是可行,但是不推荐) System.out.println(s.getMax(13,34)); } }
使用场景:
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
static的注意事项:
- 静态方法只能访问静态的成员。不可以直接访问实例成员
- 实例方法可以访问静态的成员,也可以访问实例成员
- 静态方法中不可以出现this关键字
代码演示:
public class Test3 { /** 静态成员 */ public static int onlineNumber = 10; public static void test2(){ System.out.println("==test2=="); } /** 实例成员 */ private String name; public void run(){ System.out.println(name + "跑得快"); } //3、静态方法中不可以出现this关键字 public static void test3(){ //System.out.println(this); //this只能代表当前对象!!静态对象可以不用对象调用 } //2、实例方法可以访问静态的成员,也可以访问实例成员 public void go(){ System.out.println(Test3.onlineNumber);//类名访问也可以 System.out.println(onlineNumber); test2(); System.out.println(name); run(); } //1、静态方法只能访问静态的成员。不可以直接访问实例成员 public static void test(){ System.out.println(Test3.onlineNumber);//类名访问也可以 System.out.println(onlineNumber); test2(); //System.out.println(name);//不可以直接访问实例成员 //run();//不可以直接访问实例成员方法 } public static void main(String[] args) { //目标:理解static 访问相关的语法:面试笔试题,或者以后理解程序很重要的知识(拓展)。 } }
static应用知识:工具类
- 类(内部)里都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
好处:一次编写,处处可用,提高代码的重用性。
要求:建议工具类的构造器私有化处理。
案例1:
在企业管理系统里,通常需要一个系统的很多业务处使用验证码进行防刷新等安全控制。
/** 工具类:内部是一些静态方法。每个方法完成一个功能为目的。 好处:一次编写,处处可用,提高代码的重用性。 要求:建议工具类的构造器私有化处理 */ public final class Util { /** 注意:由于工具类无需创建对象,所以把其构造器私有化会显得很专业 */ private Util(){ } /** 静态方法 */ public static String createVerifycode(int n ){ //开发一个验证码 //1、定义一个变量记住验证码 String code = "" ; //2、定义一个变量记住全部验证码字符 String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; //3、定义一个循环生成几个随即索引,去得到几个字符 Random r = new Random(); for (int i = 0; i < n; i++) { //4、获取随即索引对应的字符,连接给code int index = r.nextInt(data.length()); code += data.charAt(index); } return code; } }
public class Login { public static void main(String[] args) { //开发一个验证码 System.out.println(Util.createVerifycode(6)); //Util util = new Util();//构造器私有之后不能构建对象。显得更专业。 } }
为什么工具类中的方法不用实例方法做?
- 实例方法需要创建对象调用。
- 此时用对象只是为了调用方法,这样会浪费内存。
工具类定义时的其他要求:
由于工具类里面都是静态方法,直接使用类名即可访问。因此,工具类无需创建对象,建议将工具类的构造器进行私有。
案例2:
设计一个数组的工具类
/** 练习:完成数组的工具类的设计 */ public class ArrayUtils { /** 私有构造器 */ private ArrayUtils(){ } /** 定义工具方法:静态方法 */ //toString方法:用于返回整数数组的内容 public static String toString(int[]arr){ //1、一些校验 if (arr == null ){ return null; } //2、拼接内容并返回 String result = "[" ; for (int i = 0; i < arr.length; i++) { result += (i == arr.length-1 ? arr[i] : arr[i] + ","); } result += "]"; return result; } //getAerage方法:统计平均值(平均值为去掉最低分和最高分后的分值) public static double getAweage(int[]arr){ if (arr.length !=0){ //1、找出数组中的最高分和最低分 int max = arr[0]; int min = arr[0]; int sum = 0; for (int i = 0; i < arr.length; i++) { if (max < arr[i]){ max = arr[i]; } if (min > arr[i]){ min = arr[i]; } //总分 sum += arr[i]; } //3、求平均值 double result = (sum - max - min) *1.0/ (arr.length - 2); return result; } return 0; } }
public class TestDemo2 { public static void main(String[] args) { int[]arr = null; int[]arr1 = {}; int[]arr2 = {12,23,44,99}; System.out.println(ArrayUtils.toString(arr)); System.out.println(ArrayUtils.toString(arr1)); System.out.println(ArrayUtils.toString(arr2)); System.out.println(ArrayUtils.getAweage(arr2)); } }
static应用知识:代码块
代码块概述:
- 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
- 在Java类下,使用{}括起来的代码被叫做代码块。
代码块分类:
- 静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候做一些静态数据初始化的操作,以方便后续使用。
public class StaticDemo1 { public static String schoolName; /** 静态代码块:有static修饰,属于类,与类一起优先加载一次,自动触发执行。 作用:用于初始化静态资源。 */ static { System.out.println("-----静态代码块被触发执行了-----");//先运行 schoolName = "黑马"; } public static void main(String[] args) { //目标:先理解静态代码块 System.out.println("-----main方法执行-----");//后执行 System.out.println(schoolName); } }
- 构造代码块(了解,见的少)
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行。
使用场景:初始化实例资源。
public class StaticDemo2 { private String name; public StaticDemo2(){ System.out.println("===无参构造器被触发执行===");//在执行 } /** 实例代码块:无static修饰,属于对象,每次构建对象时,都会触发一次执行。 作用:初始化实例资源 */ { name = "张三"; System.out.println("=====实例代码块被触发执行=====");//构建对象时,先执行 } public static void main(String[] args) { //目标:先理解实例代码块(构造代码块)。 StaticDemo2 s1 = new StaticDemo2(); System.out.println(s1.name);//最后执行的 StaticDemo2 s2 = new StaticDemo2(); System.out.println(s2.name); } }
练习案例:
斗地主游戏:提前准备好54张牌,后续才可以直接使用这些牌数据。
public class StaticTest3 { /** 1、定义一个静态的集合存储54张牌,这样这个集合只加载一个。因为当前房间只需要一副牌。 */ public static ArrayList<String> cards = new ArrayList<>(); /** 2、在程序真正运行main方法前,把54张牌放进去吧,后续游戏可以直接使用了。 */ static { //3、正式做牌,放到集合中去即可。 //a、定义一个数组存储全部点数:类型确定了,个数确定了。 String[] sizes = {"3" , "4" , "5" ,"6" , "7" , "8" ,"9" , "10" , "J" ,"Q" , "K" , "A" , "2"}; //b、定义一个数组存储全部花色:类型确定了,个数确定了。 String[] colors = {"♥" , "♠" , "♦" , "♣"}; //c、遍历点数 for (int i = 0; i < sizes.length; i++) { //size[i] //d、遍历花色 for (int j = 0; j < colors.length; j++) { //colors[j] //一张牌 String card = sizes[i] + colors[j]; cards.add(card); } } //e、单独加入大小王。 cards.add("大🃏"); cards.add("小🃏"); } public static void main(String[] args) { //目标:模拟游戏启动前,初始化54张牌数据。 System.out.println("新牌:" + cards); } }
static应用知识:单例设计模式
什么是设计模式(Design pattern)
- 开发里经常会遇到一些问题,一个问题通常会有n种解决办法,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称为设计模式。
- 设计模式有20多种,对应20多种软件开发种会遇到的问题。
- 学设计模式主要学习两点:
1、这种模式原来解决什么问题。
2、遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例设计模式:
- 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
- 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。
单例的实现方式很多。。。。
- 饿汉单例模式
在用类获取对象的时候,对象已经提前创建好了。
设计步骤:1、定义一个类,把构造器私有。
2、定义一个静态变量存储一个对象。
/** 使用饿汉单例实现单例类 */ public class SingleInstance { /** 2、饿汉单例是在获取对象前,对象已经提前准备好了一个。 这个对象只能是一个,所以定义静态成员变量记住。 */ public static SingleInstance instance = new SingleInstance(); /** 1、必须把构造器私有化。 如果不私有化,可以随意的创建对象,就不能实现单例了 */ private SingleInstance(){ } }
public static void main(String[] args) { //目标:理解饿汉单例的设计步骤。 SingleInstance s1 = SingleInstance.instance; SingleInstance s2 = SingleInstance.instance; System.out.println(s1 == s2 );//因为每次拿对象,拿的是同一个对象,所以返回true。 }
- 懒汉单例模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:1、定义一个类,把构造器私有。
2、定义一个静态变量存储一个对象即可。不能new对象,new对象的话变成了恶汉单例。
3、提供一个方法返回单例对象。
/** 懒汉单例:在真正需要该对象的时候,才去创建一个对象(延迟加载对象) */ public class SingleInstance2 { /** 2、定义一个静态的成员变量负责存储一个对象。 只加载一次,只有一份。 注意:最好私有化,这也昂可以避免给别人挖坑! */ private static SingleInstance2 instance; /** 3、提供一个方法,对外返回单例对象。 */ public static SingleInstance2 getInstance(){ if (instance == null){ //第一次来拿对象,此时需要创建对象 instance = new SingleInstance2(); } //不是空的话,就不能创建对象了。 return instance; } /** 1、构造器私有 */ private SingleInstance2(){ } }
public class Test2 { public static void main(String[] args) { //目标:掌握懒汉单例的设计,理解其思想。 SingleInstance2 s1 = SingleInstance2.getInstance(); SingleInstance2 s2 = SingleInstance2.getInstance(); System.out.println(s1 == s2);//true } }
static的案例:
创建一个球员类,该类最多只允许创建11个对象,提示利用static和封装性来完成。
/** * 创建一个球员类,并且该类最多只允许创建十一个对象。提示利用 static 和 封装性来完成。 */ public class Player { /** * 球员号码 */ private int number; /** * 球员名字 */ private String name; /** * 球员年龄 */ private int age; /** * 球员数量 */ private static int count = 0; /** * 临时变量。最多创建的球员数11 */ public static int num = 11; /** * 构造函数私有化,不允许自己创建对象 * */ private Player() { count++; } private Player(int number, String name,int age) { this.number = number; this.name = name; this.age = age; count++; } /** * 方法1 * 定义一个判断是否已经创建了11个对象的方法 * @return count<11时返回创建的对象 * >11时返回null */ public static Player CreatePlayer(){ //count小于11时,可以创建新的对象 if (count < num){ return new Player(); }else{ //大于11个,就返回null return null; } } public static Player CreatePlayer(int number, String name,int age){ //count小于11时,可以创建新的对象 if (count < num){ return new Player(number,name,age); }else{ return null; } } /** * 方法2 * 创建新的对象的方法 * @return */ public static Player CreatNew(){ //count小于等于11时,可以创建新的对象 if (count<num){ System.out.println("你的第"+ (count + 1) + "个Player对象已经创建"); return new Player(); }else{ System.out.println("创建对象失败,最多创建"+ num +"个"); return null; } } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } 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 static int getCount() { return count; } }
public class PlayTest { public static void main(String[] args) { while (true){ Player player = Player.CreatePlayer(); if (player!=null){ System.out.println("创建了第"+ Player.getCount() +"个运动员对象"); }else{ System.out.println("已经创建了"+ Player.num +"个对象,不能再创建了。"); break; } } /*while (true){ Player player = Player.CreatNew(); if (player == null){ break; } }*/ } }
复习:static——静态
凡是由static修饰的东西,都具有两个共同特点:
1、与对象无关,与类有关;
2、会在类加载期有特殊动作
static属性
普通属性再内存里存放在每一个对象身上的。也就是说:当我们new一个对象,在这个对象的存放空间里就会声明一个普通属性;new多个对象,每个对象的存放空间里面各有一个该属性。所以,我们操作哪个对象就操作该对象身上的该属性,而不会影响其他对象。所以这种属性又叫做“成员属性”,因为它是组成对象的一部分。
static属性:它不是存在于每个对象身上的,而是全类共享一个值。该属性是在类加载期就把这个属性产生了,并放在内存中的静态区。之后无论在代码里面我们用对象访问该属性,还是用类名访问该属性,都是指向同一个空间。
static方法
普通方法:是与对象相关的
这个相关体现在两个地方:
1、该方法的调用,必须用对象.的方式;
2、该方法的实现部分,能够操作到该对象的普通属性或普通方法(关键)。当然静态属性和静态方法也可以。
static方法:是与对象无关的,同样体现在2处:
1、该方法的调用,可以使用类名.的方式,且用类名.和用对象.没有差异;
2、该方法的实现部分,只能调用到该方法的静态属性和静态方法。
这两个差异的理解:1、从场景上看,既然这个方法是静态的,也就是说它的执行与对象无关,那么在它内部也就不应该知道到底在操作哪个当前对象,也就不能确定这个对象相关的属性和方法;
2、从底层实现看,静态方法在加载期是被优先加载的,也就是说在加载静态方法的时候,还没有加载这个类里面的非静态内容,而如果这个时候静态方法里面调用了非静态内容,JVM会不认识,所以编译报错。
我们在自己写代码时要注意,只有工具类的工具方法才能写出static
2、继承(面向对象三大特征之二):
有些人把抽象也算作对象的第4个特点。
什么是继承?
让拥有is-a关系的类能够共享属性和行为,达到代码的共享。
Java中提供一个关键字extemds,使用这个关键字,我们可以让一个类和另一个类建立起父子关系。
如:public class Student extends People{}
Student称为子类(派生类),People称为父类(基类或超类)。
作用:
当子类继承父类后,就可以直接使用父类公共的属性和方法了
Java中子类更强大。继承的优点和缺点
优点 :
可以提高代码的复用性,减少代码冗余,增强类的功能扩展性。
子类可以扩展自己的属性和方法
子类可以使用父类的非 private的属性和方法.
代码的设计更加的简单
缺点:
增加了耦合性(两个类的关联度)
继承可以打破封装
继承设计规范:
子类们相同特性(共同属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义
在子类自己里。
原因:如果子类的独有属性、行为定义在父类中,会导致其他子类也会得到这些属性和行为,不符合面向对象逻辑。代码演示:
人类:
/** 人类:作为父类 */ public class People { public void run(){ System.out.println("人会跑---"); } }
学生类:
/** 学生类:作为子类 */ public class Student extends People{ }
测试类:
public class Test { public static void main(String[] args) { // 目标:认识继承的这种关系。搞清楚使用继承的好处。 Student s = new Student(); s.run(); } }
继承的设计规范案例:
教学管理系统中,存在学生、老师角色会进入系统。
分析:
学生信息和行为(名称、年龄、所在班级、查看课表、填写听课反馈)
老师信息和行为(名称、年龄、所在部门名称、查看课表、发布问题)
定义人类最为父类包含属性(名称、年龄),行为(查看课表)
/** 父类 */ public class People { private String name; private int age; /** 查看课表 */ public void queryCourse(){ System.out.println(name + "在查看课表---"); } 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 Student extends People{ /** 独有的信息:所在班级 */ private String class1; public String getClass1() { return class1; } public void setClass1(String class1) { this.class1 = class1; } /** 独有的行为,填写反馈信息 */ public void writeInfo(){ System.out.println(getName() + "写下了,学习语法,好哈皮---"); } }
定义子类:老师类类包含属性(所在部门名称),行为(发布问题)
/** 子类 */ public class Teacher extends People { /** 独有的信息:所在部门 */ private String department; public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } /** 独有的行为:发布问题 */ public void postQuestions(){ System.out.println(getName() + "发布了----问题"); } }
测试类:
public class Test { public static void main(String[] args) { //目标:理解继承的设计思想。 Student s = new Student(); s.setName("蜘蛛精");//使用父类的 s.setAge(999);//使用父类的 s.setClass1("通信1804班"); System.out.println(s.getName());//使用父类的 System.out.println(s.getAge());//使用父类的 System.out.println(s.getClass1());//使用子类的 s.queryCourse();//父类的 s.writeInfo();//子类的方法 Teacher t = new Teacher(); t.setName("张三");//父类的 t.setAge(23);//父类的 t.setDepartment("语文部"); System.out.println(t.getName());//父类的 System.out.println(t.getAge());//父类的 System.out.println(t.getDepartment());//子类自己的 t.queryCourse();//父类的 t.postQuestions();//子类的方法 } }
继承的特点:
- 子类不能继承父类的构造器,可以继承父类的属性和行为。
- java是单继承模式:一个类只能继承一个直接父亲
- java只支持单继承(子类只能继承一个直接父类),不支持多继承(子类不能同时继承多个父类),但是支持多层继承(子类A继承父类B,父类B可以继承父类C)。
- java中所有的类都是object类的子类
单继承:
在面向对象的概念里,是没有单继承还是多继承这种说法的。只是说Java语言在设计它的实现的时候,采用了单继承;也有别的编程语言采用了多继承。
多继承的好处:丰富度。
坏处:复杂度。我们在复杂情况下设计出来的类结构图一定会呈现出网状结构。
单继承的好处:无论多复杂的情况,设计出来的类层次结构一定是简单的树状结构。
坏处:丢失了丰富度。所以也就有了接口的多实现来进行补充。
object类特点:
java所有类,要么直接继承了object,要么默认继承了object,要么间接继承了object,object类是祖宗类。
有一句话: 少继承 多实现。
所有的类和数组都继承于这个类,它是根也是祖宗。它的属性和行为是所有类都共有的。也就是说先人在设计的时候是把共用性最强的行为写在它当中了。所以这个类是唯一一个我们要把它所有方法都要进行掌握的一个类。
在目前这个阶段至少要掌握两个方法的使用和知道一个方法:
1、equals()——经常重写
作用:判断两个对象是否相等。
那么和”==“号有区别吗?
==是用来比较两个对象引用是否是同一个,或者说两个引用是否指向同一个对象。
equals是用来比较两个对象的内容是否相等。
只是由于原生的equals方法是定义在Object当中的,所以它不知道子类有哪些内容,所以在Object当中的实现只能是返回”==“号比较的结果。而我们通过继承,子类会自动具备来自于Object的这个方法,然后通过重写我们自己定义比较规则。
2、toString()
作用:返回对象的字符串描述。
原生的toString方法是定义在Object当中的,同样不知道子类有哪些内容,当然也就没有办法确认子类对象需要的字符串描述,所以只能采用统一的”类型@对象的引用值“
我们自己写的类需要通过重写,然后返回一个我们自定义的字符串。
另外toString方法在对象做字符串操作的时候会被自动调用,无需我们显式调用
3、finallize()
在JDK9之后过时了
作用:在JDK9之前用来销毁对象的方法。我们可以把它和构造方法看成一对。由于Java出于安全性的考虑,不让我们操作对象的销毁,所以它把销毁方法写好,放在Object里,然后所有的类和数组通过继承都拥有了销毁方法。我们都知道Java的对象回收都是由GC(垃圾回收机制)来进行操作的,而GC正是通过调用这个方法来完成对象的销毁。
记住:finallize方法是定义在Object当中的,由GC调用。
这个方法通常不重写,因为你不知道如何重写。如果有需要重写,那么在写完自己的实现后调用一下super.finalize()完成父类的实现。
4、重写——子类把父类的方法给重写实现
重写的规范:方法名必须一致;
参数列表必须一致(包括:参数的个数,参数的类型和参数的顺序,没有参数名);
返回类型可以变的,但是重写之后只能返回重写前的返回类型或它的子类型;
访问修饰符也可以变,但是子类重写后的访问修饰符只能往大的方向变或不变。
public class Test { public static void main(String[] args) { //目标:理解继承的特点 //1、子类不能继承父类的构造器,可以继承父类的属性和行为。 //2、java是单继承模式:一个类只能继承一个直接父亲 //3、java不支持多继承,但是支持多层继承。 //4、java中所有的类都是object类的子类 //object类特点:java所有类,要么直接继承了object,要么默认继承了object,要么间接继承了object,object类是祖宗类。 //子类是否可以继承父类的私有成员?可以的,只是不能直接访问。 Tiger t = new Tiger(); //t.eat(); //子类是否可以继承父类的静态成员? 我认为不算继承的。只是共享的。 System.out.println(Tiger.location); } } /** * 父类 */ class Animal{ private void eat(){ System.out.println("动物要吃东西---"); } public static String location = "长隆动物园"; } /** * 子类 */ class Tiger extends Animal{ }
继承后:成员变量、成员方法的访问特点
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
就近原则:子类(局部)没有找子类(成员),子类(成员)没有找父类,父类没有就报
错。
如果子父类中出现了重名的成员,此时如果一定要在子类中使用父类怎么办?
格式:super.父类成员变量/父类成员方法public class Test { public static void main(String[] args) { //目标:理解继承后成员的访问特点:就近原则。 Dog d = new Dog(); d.run();//狗跑的贼快---。子类的 d.lookDoor();//狗可以看门---。子类的 d.showName(); } } class Animal{ public String name = "动物名"; public void run(){ System.out.println("动物可以跑---"); } } class Dog extends Animal{ public String name = "狗名"; public void lookDoor(){ System.out.println("狗可以看门---"); } public void showName(){ String name = "局部名"; System.out.println(name);//局部名 System.out.println(this.name);//当前子类对象的name(狗名) System.out.println(super.name);//找父类的name(动物名) super.run();//找父类方法(动物可以跑---) run();//子类的run(狗跑的贼快---) } public void run(){ System.out.println("狗跑的贼快---"); } }
继承后:方法重写
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的
方法。
什么情况下需要进行方法的重写?
当子类的实现方式跟父类的实现方式不一样的时候,就需要对父类的方法进行重写( 覆盖).
当子类重写了父类的方法后,则会覆盖父类原来方法的实现。
方法重写应用场景:
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。;子类可以重写父类中
的方法。
@Override重写注解
1、重写校验注解,加上这个,这个方法必须是正确重写的,这样更安全。
2、提高程序的可读性,代码优雅!
加上该注解后如果重写错误,编译阶段会出现错误提示。
方法重写注意事项和要求:
1、重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
2、私有方法不能被重写。
3、子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public)
4、子类不能重写父类的静态方法,如果重写会报错的。重写和重载的区别
重载: 方法必须在一个类中,方法名相同, 但是参数列表不相同.
重写: 两个类,并且两个类之间必须有继承或者实现关系. 方法的访问修饰符必须不小于父类的反问修饰符。
方法的返回类型(必须要小于或等于父类的返回类型。前提是引用数据类型) 方法的名字要一样,甚至,方法的参数列表必须要一样。
案例:
旧手机的功能只能是基本的打电话,发信息
新手机的功能:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片。
public class Test { public static void main(String[] args) { //目标:认识方法重写。:在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。 //方法重写应用场景:当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。;子类可以重写父类中的方法。 NewPhone hw = new NewPhone(); hw.call(); hw.sendMsg(); } } /** 新手机:子类 */ class NewPhone extends Phone{ //重写的方法 @Override //1、重写校验注解,加上这个,这个方法必须是正确重写的,这样更安全。2、提高程序的可读性,代码优雅! //加上该注解后如果重写错误,编译阶段会出现错误提示 //方法重写注意事项和要求:1、重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。2、私有方法不能被重写。 //3、子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public) //4、子类不能重写父类的静态方法,如果重写会报错的。 public void call(){ super.call(); //先用它爸爸的基本功能 System.out.println("开始视频通话--"); } //重写的方法 @Override public void sendMsg(){ super.sendMsg();//先用它爸爸的基本功能 System.out.println("发送有趣的图片--"); } //注意:静态方法不能被重写。 // @Override // public static void test(){ // } } /** 旧手机:父类的 */ class Phone{ public void call(){ System.out.println("打电话-"); } public void sendMsg(){ System.out.println("发短信-"); } public static void test(){ } }
继承后:子类构造器的特点
子类的全部构造器默认会先访问父类的无参数构造器,再执行自己。
父类:
public class Animal { public Animal(){ System.out.println("父类Animal无参数构造器被执行-"); } }
子类:
public class Dog extends Animal{ public Dog(){ super();//写不写都有,默认找父类的无参数构造器执行。 System.out.println("子类Dog无参数构造器被执行-"); } public Dog(String name){ super();//写不写都有,默认找父类的无参数构造器执行。 System.out.println("子类Dog有参数构造器被执行-"); } }
测试类:
public class Test { public static void main(String[] args) { //目标:认识继承后子类构造器的特点 //特点:子类的全部构造器默认会先访问父类的无参数构造器,再执行自己 //原因:1、子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。 //2、子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。 //怎么调用父类构造器的?子类构造器的第一行语句默认都是:super(),不写也存在。 Dog d1 = new Dog(); System.out.println(d1); System.out.println("------------------"); Dog d2 = new Dog("金毛"); System.out.println(d2); /* 父类Animal无参数构造器被执行- 子类Dog无参数构造器被执行- com.itheima.d10_extends_constructor.Dog@1b6d3586 ----------------- 父类Animal无参数构造器被执行- 子类Dog有参数构造器被执行- com.itheima.d10_extends_constructor.Dog@4554617c */ } }
继承后:子类构造器访问父类有参构造器
super调用父类有参数构造器的作用:
- 初始化继承自父类的数据。
作用:通过调用父类有参数构造器来初始化继承自父类的数据。
父类:
public class People { private String name; private int age; public People() { } public People(String name, int age) { 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; } }
子类:
public class Teacher extends People{ public Teacher(String name , int age){ //调用父类的有参数构造器,初始化继承自父类的数据 super(name,age); } }
测试类:
public class Test { public static void main(String[] args) { //目标:学习子类构造器如何去访问父类有参数构造器,还要清楚其作用。 //super调用父类构造器 //作用:通过调用父类有参数构造器来初始化继承自父类的数据。 Teacher t = new Teacher("张三",18); System.out.println(t.getName()); System.out.println(t.getAge()); } }
this和super总结
this:代表本类对象的引用;super(超类或者父类 ):代表对父类的引用。
this的总结在这个链接里的第六点:
JavaSE基础笔记——Javaoo(面向对象)_小曹爱编程!的博客-CSDN博客JavaSE基础笔记——Javaoo(面向对象)https://blog.csdn.net/weixin_62993347/article/details/125825317?spm=1001.2014.3001.5501
super 有3中用法:
super.属性 : 调用父类对象的属性(变量)
super.成员方法(参数列表) : 调用父类的某个成员方法
super(参数列表) : 调用父类的某个构造函数
当子类被创建时. 被自动的调用父类的 空构造函数 super( )
this(...)和super(...)使用注意点:
1、子类通过this(...)去调用本类的其他构造器,本类其他构造器会通过super去手动调节父类的构造器,最终还是会调用父类构造器的。
2、注意:this(。。。)super(。。。)都只能放在构造器的第一行,所以二者不能共存在同一个构造器里。