练习目录
- 1 工具类
- 数组工具类Arrays
- ● 练习
- 用Arrays相关的API,将一个随机字符串中的所有字符升序排序,并倒序打印
- 数学工具类Math
- ● 练习
- 计算在-10.8到5.9之间,绝对值大于6 或者小于2.1 的整数有多少个?
- 2 继承
- 继承概述
- 继承的格式
- 继承中成员变量的访问特点
- 区分子类方法中重名的三种变量
- 继承中成员方法的访问特点
- 继承中方法的覆盖重写-概念与特点
- 继承中方法的覆盖重写-注意事项
- 继承中方法的覆盖重写-应用场景
- 继承中构造方法的访问特点
- super关键字的3种用法
- this关键字的3种用法
- super和this图解
- Java继承的三个特点
- ● 练习
- 1. 父类是Employee,子类是Teacher,Assistant。判断main中调用对错
- 2. 判断父子类,重名变量,重名方法调用的运行结果
- 3. 练习,写Phone类,newPhone继承。Phone中3个方法,
- 4. 子类构造方法,判断对错并修改,输出结果
- 5. 判断构造方法调用对错,改正
- 6. 写父子类并继承,重写一个父类方法。使用super和this的三种用法
1 工具类
数组工具类Arrays
java.util.Arrays
是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操作public static String toString(数组);
将参数数组变成了字符串,按照默认格式[元素1,元素2,……]public static void sort(数组);
按照默认升序(从小到大)对数组的元素进行排序。
备注:
- 如果是数值,sort默认按照升序从小到大
- 如果是字符串,sort默认按照字母升序。即挨个比较每个字符,若前半部分都相同,长的更大。到不同的哪一位那个字符大算哪个字符串大,相当于每个字符的权重依次降低。
- 如果是自定义的类型,那么这个自定义的类需要有Comparable或者Comparator接口的支持。
● 练习
用Arrays相关的API,将一个随机字符串中的所有字符升序排序,并倒序打印
package code02;
import java.util.Arrays;
import java.util.Random;
/**
* Created with IntelliJ IDEA.
*
* @Author: 雪雪子
* @Date: 2024/01/26/17:31
* @Description:
*/
public class Class01 {
// 用Arrays相关的API,将一个随机字符串中的所有字符升序排序,并倒序打印
/*生成一个随机字符串
*/
public static String generateStr(int maxSize, int maxValue){
//已知,字符串范围0,65535。生成这个范围内的ASCII码即可
//练习,用两种方法生成随机数
// Random r = new Random();
// int size = r.nextInt(maxSize + 1);
// char[] ch = new char[size];
// for (int i = 0; i < size; i++) {
// ch[i] = (char)(r.nextInt(65536));
// }
//为了方便打印,还是取32~126
if(maxValue > 95){
System.out.println("重新输字符串最大值,为了方便打印不要超过95");
return null;
}
int size = (int)(Math.random() * (maxSize + 1));
int[] arr = new int[size];
String str = new String();
for (int i = 0; i < size ; i++) {
arr[i] = (int)(Math.random() * maxValue) + 32;
str += (char)arr[i];
}//不用Arrays.toString()是因为会加字符 [ ]和,
return str;
}
}
public class Demo01 {
public static void main(String[] args) {
String str1 = Class01.generateStr(20,95);
System.out.println(str1);
char[] ch = str1.toCharArray();
Arrays.sort(ch);
for (int i = ch.length - 1; i >= 0; i--) {
System.out.print(ch[i] + ",");
}
}
}
快捷写法,对应快速for循环遍历的,fori
,forr
恰好是从尾遍历
数学工具类Math
java.util.Math类是数学相关的工具类,提供了大量的静态方法public static double abs(double num);
获取绝对值public static double ceil(double num);
向上取整public static double floor(double num);
向下取整public static long round(double num);
四舍五入Math.PI
代表近似的圆周率常量(double)
Ctrl + F12 找到Math 中的PI
● 练习
计算在-10.8到5.9之间,绝对值大于6 或者小于2.1 的整数有多少个?
A:
double left = -10.8;
double right = 5.9;
int count = 0;
for(double i = Math.ceil(left); i <= Math.floor(right); i++){
double abs = Math.abs(i);
if(abs > 6 || abs < Math.ceil(2.1)){
count++;
System.out.println(i);
}
}
2 继承
继承概述
面向对象的三大特征:封装性,继承性,多态性。
继承是多态的前提,如果没有继承,就没有多态
继承主要解决的问题就是:共性抽取
继承关系中的特点:
- 子类可以及拥有父类的“内容”
- 子类还可以拥有自己专有的内容
继承的格式
“子类就是一个父类”,例如:父类是Humen人类,子类是Student学生,学生是一个人,关系“is - a” a teacher is a employee
定义父类
public class 父类名{
//……
}
定义子类格式:
public class 子类名 extends 父类名{
//……
}
子类中就算什么都不写,也会继承父类的方法和变量,可以直接子类对象.
调用父类的成员
继承中成员变量的访问特点
Fu{
int numFu = 10;
int num = 100;
}
Zi extends Fu{
int numZi = 20;
int num 200;
}
fu.numFu;
Fu对象只能使用父类的成员,没有任何子类的内容。zi.numFu; zi.numZi;
Zi对象可以使用父的成员
● 若父与子中成员重名
根据创建子类对象的不同, 访问有两种方式:
- 直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。
Zi zi = new Zi();
zi.num//200,优先子类。因为创建zi时等号左边的引用是zi,所以是子类的num
//例如zi.numFu子类中未定义numFu,就向上找到父类中
- 间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找Fu中methodFu()
用来输出num,为100
使用的num是本类当中的,不会向下找子类的Zi中methodZi()
输出num,为200
使用的num是本类当中的,因为本类中有num,若无才向上找。
zi.methodZi();//num = 200
这个方法是子类的,优先用子类的num,若无才向上找。zi.methodFu();//num =100
,子调用父方法,这个方法是在父类中定义的。
区分子类方法中重名的三种变量
Fu{
int num = 10;
}
Zi extends Fu{
int num = 20;
public void method(){
int num = 30;
}
}
在method方法中区分访问:
访问局部变量:直接写成员变量名,就近原则,优先用局部
本类的成员变量:this.成员变量名
父类的成员变量:super.成员变量名
继承中成员方法的访问特点
Fu{
methodFu(){
}
method(){
sout("父类重名方法执行")
}
}
Zi extends Fu{
methodZi(){
}
method(){
sout("子类重名方法执行")
}
}
同样的,Fu对象只能使用methodFu()方法,Zi对象可访问使用两个方法。
● 若父与子方法重名
都叫method
创建的对象(等号右边)是谁就用谁的成员方法,无论是成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类的。
Zi zi = new Zi();
zi.method();//调用的子类method,因为new Zi();
继承中方法的覆盖重写-概念与特点
- 概念:在继承关系当中,方法的名称一样,参数列表也一样
- 重写(Override):方法的名称一样,参数列表也一样
- 重载(Overlood):方法的名称一样,参数列表不一样
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。(类似上文中父与子之间的重名方法)
new的是谁就用谁的
继承中方法的覆盖重写-注意事项
Fu,Zi类中都定义method()
- 必须保证父子类之间方法的名称相同,参数列表也相同。@Override,写在方法前面,用来检测是不是有效的正确覆盖重写。写不写都可以,写了可以帮忙检查正确与否,防止写为其他方法或写错参数
- 子类方法的返回值必须 <= 父类方法的返回值范围。(扩展: java.lang.Object类是所有类的公共最高父类(祖宗类))必须和被重写方法的返回值类型相同,或者是返回类型的子类型。类似,父中方法返回值Father,子中重写方法返回值Son。java.lang.String就是Object的子类
疑问 void 怎么算大小? - 子类方法的权限必须 >= 父类方法的权限修饰符。
== public > protected > (default) > private==
备注:(default)不是关键字default,而是什么都不写,留空。
继承中方法的覆盖重写-应用场景
例如,要设计一个新款手机。而老款手机的类中有方法:打电话,发短信,来电显示。而新手机只需要添加新的功能方法就可以,比如来电显示可以显示姓名和头像,所以重写父类老款手机的方法即可。
- 设计原则:对于已经投入使用的类,尽量不要进行修改。推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。即继承
- 尽量不要修改老方法,用重写调用老方法,再加新功能。类比到大规模情况,这一部分可能有几百行,即几百行重复代码,所以重写中super调用父类方法,非常有必要。
继承中构造方法的访问特点
在父类中定义一个无参构造,子类中定义一个无参构造
当new子类对象时,会先执行父构再子构
- 因为 !!子类的构造方法中隐含的赠送了一句super(); 会调用父类的无参构造。不论有参还是无参,如果不指定super的参数子类所有的构造方法都隐含一句super();。
- 若Fu中只写了有参构造就只有有参构造(复习:写构造方法时,若不写,系统赠送一个无参构造。若写了有参构造则不赠送,写了无参构造也不赠送无参构造,以自定义的为准)则子类构造方法内需手动调用super(参数)。 不写的话系统又赠送super()在子类构造方法中,就会报错。因为父类并没有无参构造。
- 只有子类构造方法,才能调用父类构造方法,且必须是第1句,连写2句,第2句就错。
总结:
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的"super()"调用,所以一定是先调用父类构造,后执行子类构造
- 子类构造可以通过super关键字来调用父类重载构造(即无参,有参……)
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定super调用,super只能有一个,还必须是第一个
super关键字的3种用法
- 在子类的成员方法中,访问父类的成员变量。 super.num
- 在子类的成员方法中,访问父类的成员方法。super.method();
- 在子类的构造方法中,访问父类的构造方法。super();
this关键字的3种用法
this关键字用来访问本类内容
- 在本类的成员方法中,访问本类成员变量
区分方法中的局部变量,如在子类方法中的num,this.num,super.num - 在本类的成员方法中,访问本类的另一个成员方法。
class Zi extends Fu{
methodA(){
sout("AA");
}
methodB(){
● this.methodA();
}
}
- 在本类的构造方法中,访问本类的另一个构造方法。不能自己调自己
Zi(){
//this();
//错误写法,不能自己调自己
}
Zi(){
this(10);//重载调用
}
注意:
A. this (……)调用也必须是构造方法的第一个语句,也就是唯一一个。
B. super和this两种构造调用,不能同时使用。
即调用了this构造,super()将不再赠送
super和this图解
public class Fu {
int num = 10;
void method(){
System.out.println("Fu的method方法");
}
}
public class Zi extends Fu{
int num = 20;
@Override
public void method(){
System.out.println("重写的Zi的method方法");
}
void show(){
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
public class Demo01 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
zi.method();
}
}
分析以上代码在内存中运行过程
先编译到方法区,三个类加载其方法,属性。
执行main方法,进栈,new出的对象在堆。
后执行的方法后压栈,执行结束后依次弹出。
Java继承的三个特点
- Java语言是单继承的,一个类的直接父类只能有唯一的一个。
Q: 为什么不能继承两个父
A: 若两个父有重名方法,继承后无法分辨是哪个父的,会混乱。 - Java语言可以多级继承
class A{}
class B extends A{}
class C extends B{}
称呼A,B都为C的父类,B为C的直接父类
当new C时,会依次执行A,B,C的构造器,因为B中super();调A的构造器,然后自己B的,C中super()也是调B的,B又调A的。 - 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
● 练习
1. 父类是Employee,子类是Teacher,Assistant。判断main中调用对错
public class Employee {
static int sNum2;
int num;
public void method(){
System.out.println("Employee类的method方法");
}
public static void method2(){
System.out.println("Emoloyee类的静态method2方法");
}
}
public class Teacher extends Employee{
}
public class Assistant extends Employee{
}
public class Demo01 {
public static void main(String[] args) {
Assistant as1 = new Assistant();
Teacher te1 = new Teacher();
as1.method();
te1.method();
as1.num = 10;
te1.num = 20;
Assistant.sNum2 = 30;
Teacher.method2();
}
}
A: 正确,子类会继承父类的成员,不论静态还是非静态。即使什么都不写,也隐含的有父类的成员,所以子类对象或子类可以直接调用。
2. 判断父子类,重名变量,重名方法调用的运行结果
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu(){
System.out.println(num);
}
public void method(){
System.out.println("父类的重名方法");
}
}
public class Zi extends Fu{
int numZi = 20;
int num = 200;
public void methodZi(){
System.out.println(num);
}
public void method(){
System.out.println("子类的重名方法");
}
}
public static void main(String[] args) {
Zi zi = new Zi();
Fu fu = new Fu();
Fu fu2 = new Zi();
System.out.println(zi.num);
System.out.println(fu.num);
zi.methodZi();
zi.methodFu();
fu.methodFu();
zi.method();
fu.method();
System.out.println(fu2.num);
fu2.methodFu();
fu2.method();
}
A: 就记得
- 父子类中重名变量时,看创建对象等号左边是谁,就是这个类的变量。如果是通过方法间接调用变量,就看方法是属于谁,调谁的变量。
- 父子类中重名方法时,看创建对象等号右边是谁,就用谁的。
public static void main(String[] args) {
Zi zi = new Zi();
Fu fu = new Fu();
Fu fu2 = new Zi();
Zi zi2 = new Fu();
System.out.println(zi.num);//200 通过对象直接访问,就看等号左边是谁。
System.out.println(fu.num);//100
zi.methodZi();//200 通过方法间接访问变量,看方法属属于谁。
zi.methodFu();//100
fu.methodFu();//100
zi.method();//子类的重名方法
fu.method();//父类的重名方法
System.out.println(fu2.num);//100 看等号左边为Fu类
fu2.methodFu();//100 ,方法属于Fu,Fu中num为100
fu2.method();//子的重名方法,看右边new的是Zi对象,就用Zi的方法
}
3. 练习,写Phone类,newPhone继承。Phone中3个方法,
call,send,show。send方法既可以发短信给一个人,也可以多个。newPhone继承,重写show方法,增加新功能,并执行查看
A: send方法在Phone中重载,在父子类之间是重写
public class Phone {
public void call(){
System.out.println("打电话");
}
public void send(String who){
System.out.println("发短信" + who);
}
protected String[] send(String[] them){
for(int i = 0; i < them.size(); i ++){
System.out.println("发短信" + them[i]);
}
}
public void show(){
System.out.println("显示来电号码");
}
}
public class newPone extends Phone{
@Override
public void show(){
super.show();
System.out.println("显示来电人");
System.out.println("显示来电人头像");
}
}
public static void main(String[] args) {
//输出子类重写的结果
NewPhone p1 = new NewPhone();
p1.show();
}
4. 子类构造方法,判断对错并修改,输出结果
父类无参,子类无参+有参
public class Fu {
public Fu(){
System.out.println("Fu的无参构造");
}
}
public class Zi extends Fu{
int id;
public Zi(){
System.out.println("Zi的无参构造");
}
public Zi(int id){
id = this.id;
System.out.println("Zi3的有参构造");
}
}
父类有参,子类无参+有参
public class Fu2{
int age;
public Fu2(int age){
age = this.age;
System.out.println("Fu2的有参构造,age = " + age);
}
}
public class Zi2 extends Fu2 {
String id;
public Zi2() {
System.out.println("Zi2的无参构造");
}
public Zi2(String id) {
super();
super(50);
id = this.id;
System.out.println("Zi2的有参构造, id = " + this.id);
}
public Zi2(int age) {
System.out.println("Zi2的有参构造, age = " + this.age);
super(60);
}
}
public static void main(String[] args) {
/*输出子类构造时的super情况*/
Zi zi = new Zi();
Zi2 zi2 = new Zi2();
Zi2 zi3 = new Zi2(34);
Zi2 zi4 = new Zi2("九五班");
}
A:
其中Zi2的两个构造方法全错,因为父类Fu2只有有参构造,而子类构造器中不指定就会隐含一个super(),但父类并没有无参构造所以会错。
并且子类构造器中只能调用一次super,且必须是第一句。
改前改后对比:
public class Zi2 extends Fu2 {
String id;
● public Zi2() {//错误,因为父类没有无参构造,子类构造又没调用父类构造。隐含一个super(),但没有这个,所以错
System.out.println("Zi2的无参构造");
}
//改后
public Zi2() {
super(50);
System.out.println("Zi2的无参构造");
}
● public Zi2(String id) {//不可以写两句,而且没有super();
super();
super(50);
id = this.id;
System.out.println("Zi2的有参构造, id = " + this.id);
}
//改后
public Zi2(String id) {
super(50);
id = this.id;
System.out.println("Zi2的有参构造, id = " + this.id);
}
● public Zi2(int age) {//super(60)不可以写最后,必须在子类构造器的第一句且方法内唯一一句
System.out.println("Zi2的有参构造, age = " + this.age);
super(60);
}
//改后
public Zi2(int age) {
super(70);
System.out.println("Zi2的有参构造, age = " + this.age);
}
}
输出结果为:
Fu的无参构造
Zi的无参构造
Fu2的有参构造,age = 50
Zi2的无参构造
Fu2的有参构造,age = 70
Zi2的有参构造,age = 34
Fu2的有参构造,age = 60
Zi2的有参构造,id = 九五班
因为所有子类构造器隐含的会有一个super();
5. 判断构造方法调用对错,改正
public class Zi {
Zi(){
this(123);
}
Zi(int num){
this(1,2);
}
Zi(int a, int b){
this();
}
}
A: 全错,相当于构造方法自己调自己,死循环。修改:随便哪个构造方法去掉一个this调用即可。
6. 写父子类并继承,重写一个父类方法。使用super和this的三种用法
- 在子类方法中调用父类变量,父类方法。
- 调用本类方法,本类变量和方法内局部变量。
- 在子类构造方法中,调用父类构造方法,自己的重载构造方法。
- 写后,判断输出结果
A:
public class Fu {
int num = 10;
static int sNum = 20;
Fu() {
System.out.println("Fu的无参构造器");
}
Fu(int sNum) {
Fu.sNum = sNum;
System.out.println("Fu的有参构造");
}
void method() {
System.out.println("Fu的method方法");
}
static void sMethod() {
System.out.println("Fu的sMethod静态方法");
}
}
public class Zi extends Fu{
int num = 30;
Zi(){
super();//super用法1
System.out.println("Zi的无参构造");
}
Zi(int num){
this();//this用法1
System.out.println("Zi的有参构造");
this.num = num;//this用法2
}
@Override
public void method(){
System.out.println("Zi的method方法");
int num = 40;
sMethod();//复习:非静态方法中调用,只要是内部的,都直接调用。外部的,类.or对象.
System.out.println(num);
System.out.println(this.num);
}
public void method2(){
System.out.println("Zi的method2方法");
System.out.println(super.num);//super用法2
this.method();//this用法3
super.method();//super用法3
method();//写this和super主要是便于区分,如果直接写呢?
//直接调是子的,就近原则,没有再向上找。
}
}
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
zi.method2();
}
输出结果,先构造方法,然后是method方法,局部变量里就近原则,不指定this或super,直接调用的话,先输出方法内的num,然后是this的,最后super。
Fu的无参构造器
Zi的无参构造
Zi的method方法
Fu的sMethod静态方法
40
30
然后是method2方法,中间调用zi的method两次,其中直接调也是优先this的method。因为就近原则,若子类没有重写method方法,就会向上找
Zi的method2方法
10
Zi的method方法
Fu的sMethod静态方法
40
30
Fu的method方法
Zi的method方法
Fu的sMethod静态方法
40
30