JavaSE笔记_day07_代码块、继承、super()

145 阅读13分钟

一.代码块

1.代码块的概念

  • 代码块就表示一对大括号,但是定义的位置不同,功能就不同

2.局部代码块

  • 格式{  } 
  • 位置:定义在方法当中 
  • 作用:限制定义在局部代码块中的变量的作用范围 
  • 注意:
     (1)如果变量没有定义在局部变量块中,那么代码块中对变量做的操作,出了局部代码块仍然有效。 
     (2)定义在局部代码块中的变量:作用范围有限、节省内存空间 (局部代码块执行完毕,局部代码块中定义的的变量则被回收)

public class LocalCode1 {//局部代码块:{}定义在方法当中
    public static void main(String[] args) {
        int i = 10;//作用于主方法中
        {//局部代码块
            System.out.println(i);//10
            int j = 99; //作用于局部代码块中
            System.out.println(j);//99
	    i = 88;
	    System.out.println(i);//88
        }
        System.out.println(i);//88
/*      System.out.println(j);报错*/
    }
}

3.构造代码块

  • 格式{   } 
  • 位置:类中,方法外 
  • 作用:给成员变量进行赋值、 如果多个构造方法具有一部分相同的逻辑,那么这部分相同的逻辑可以写在构造代码块中 
  • 执行机制:构造代码块在创建对象的同时,JVM主动调用执行,在构造方法之前执行,每次创建对象,只执行一次。

public class Test {
    public static void main(String[] args) {
        StructureCode2 s1 = new StructureCode2();//调用空参构造
        System.out.println(s1.getName());//构造方法被调用了!张三
        StructureCode2 s2 = new StructureCode2("李四");//调用全参构造
        System.out.println(s2.getName());//构造方法被调用了!李四
    }
}
public class StructureCode2 {
    private String name;
    {//构造代码块
        name = "张三";
        System.out.println("构造方法被调用了!");//多个构造方法具有一部分相同的逻辑,那么这部分相同的逻辑可以写在构造代码块中
    }
    public StructureCode2(){
        /*System.out.println("构造方法被调用了!");*/
    }
    public StructureCode2(String name){
        /*System.out.println("构造方法被调用了!");*/
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

4.静态代码块

  • 格式:static{  } 
  • 位置:类中,方法外 
  • 作用:给静态的成员变量进行赋值、 如果一个功能仅仅在类加载进内存时,只执行一次,那么这部分功能就可以写到静态代码块中 
  • 执行机制:当.class 文件进入到内存时,JVM会主动调用一次静态代码块, 静态代码块在整个类的执行过程中,只执行一次,与创建多少的对象无关。

public class Test {
    public static void main(String[] args) {
        StaticCode3 s3 = new StaticCode3();
        System.out.println(StaticCode3.name);//静态代码块执行了!、构造代码块执行!、构造方法执行了!大美					
        StaticCode3 s4 = new StaticCode3();//构造代码块执行!、构造方法执行了!
    }
}
public class StaticCode3 {
    static String name;
    static{//静态代码块
        System.out.println("静态代码块执行了!");
        name = "大美";//给静态成员赋值
    }
    {//构造代码块
        System.out.println("构造代码块执行!");
    }
    public StaticCode3(){
        System.out.println("构造方法执行了!");
    }
}

5.总结代码块的执行顺序

  • 静态代码块(中只执行一次,与创建几次对象无关)----> 
  • 构造代码块(每次创建对象执行一次)----> 
  • 构造方法(每次创建对象执行一次)-----> 
  • 局部代码块(方法进栈运行执行)。

6.同步代码块 (多线程的环节,用于解决线程的安全问题)

二.继承

1.继承的概述

  • 指类与类之间存在父子关系 
  • 关键字:继承extends,子 extends 父 
  • 举例:classA{ } extends classB{ }

2.继承发生的场景

  • 子类向上共性抽取为父类 

3.继承的优势和弊端

  • 继承的优势: 
        a.提高代码的复用性
        b.提高代码的可维护性(代码维护:代码修改的难易程度) 
        c.继承为多态提供了前提 
  • 继承的弊端: 
       提高了类与类之间的耦合性(耦合性:指类与类之间的关系,太紧密了) 实际开发当中应该尽量做到高内聚,低耦合。

public class Demo01Extends {
    public static void main(String[] args) {
        //创建一个子类对象teacher
        Teacher teacher = new Teacher();
        //Teacher类当中虽然什么都没写,但是会继承来自父类的method方法
        teacher.work();
        //创建一个子类对象assistant
        Assistant assistant = new Assistant();
        assistant.work();
    }
}
public class Employee {
    public void work(){
        System.out.println("工作!");
    }
}
public class Assistant extends Employee {//助教
				
}
public class Teacher extends Employee{//教师
				
}

4.继承中的注意事项

  • 父类中的私有成员(成员变量、成员方法),子类不能继承使用 
       原因:private修饰的私有成员变量和方法,只能在本类使用 
  • 父类中的构造方法,不能被子类继承 
       原因:构造方法和类名称应保持一致,矛盾。

 5.成员变量在继承中的关系

  • 子类可以使用父类中的所有非私有成员变量,父类只能使用本类中定义的成员变量 
  • 变量的访问具有就近原则,使用变量时,哪个变量离着最近,优先使用哪个变量
  1. 如果方法中定义了局部变量,优先使用局部变量
  2. 如果没有局部变量,优先使用类中定义的成员变量
  3. 如果类中没有成员变量,优先使用父类中的成员变量 
  4. 如果父类中没有成员变量,那么继续找父类的父类,知道找到Object类,仍然没有才报错。 
 即:局部变量 > 成员变量 > 父类成员变量 > 再向上找父类的成员变量

public class TestZiFu {
    public static void main(String[] args) {
        Fu f = new Fu();//定义一个父类的对象
        Zi z = new Zi();//定义一个子类的对象
        System.out.println(z.a);// 子类无重名变量a之前输出9,子类有重名变量a之后输出900
        System.out.println(z.c);//100
        System.out.println(z.d);//200
    }
}
public class Fu {
    int a = 9;//普通成员变量
    private int b = 20;//私有成员变量
    static int c = 100;//静态变量
}
public class Zi extends Fu {
    //子类可以从父类中继承a=9 ,c两个成员变量
    int d = 200;
    int a = 900;//子类重新定义变量a,与继承来的变量a重名
}

6.方法在继承中的关系

  • 子类中可以重写(Override)父类中继承的方法 
   (1)什么叫做方法的重写?
           发生在子父类的继承关系中,子类可以将父类继承来的方法,重新定义 

   (2)方法重写,必须与父类的方法名相同,参数列表相同,返回值类型相同 

   (3)子类的方法权限 >= 父类的方法权限 

public > protected > [default](什么都不写,默认权限) > private 

   (4)子类在重写了父类的方法之后,调用的是子类重写的方法 

   (5)重写方法,在方法之上写 @Override 校验 

   (6)静态方法,只能继承,不能重写

在父子类的继承关系当中,创建子类对象,访问成员方法/的规则
    创建的对象是谁,就优先用谁,如果没有则向上找。					
public class Demo01ExtendsMethod {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.methodZi();//子类方法执行!
        zi.methodFu();//父类方法执行!
        //创建的是new子类对象,所以优先用子类方法
        zi.method();//子类重名方法执行
    }
}
public class Zi extends Fu {
    public void methodZi(){
        System.out.println("子类方法执行!");
    }
    @override
    public void method (){
        System.out.println("子类重名方法执行");
    }
}
public class Fu {
    public void methodFu(){
        System.out.println("父类方法执行!");
    }
    public void method (){
        System.out.println("父类重名方法执行");
    }
}

  • 说明:什么叫做方法的重载(Overload)?什么叫做方法的重写(Override)? 
       重载:在同一个类下,方法名相同,参数列表不同,与方法的返回值无关 

       重写:在子父类继承关系下,子类重写父类中的方法,要求:方法名,参数列表,返回值类型都必须和父类一致,重写的方法权限 >= 父类的权限。

7.构造方法在继承中的关系

  • 子类无法继承父类中的构造方法,但是子类的构造方法可以调用父类的构造方法 (构造方法之间调用)
  • 子类构造方法的第一行,默认有一个表达式,super();
  • 子类构造方法的第一行,也可以手动调用父类的有参构造 super("name");
  • 子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super调用
  1. super 表示父类对象的引用 super();
  2. super(); 表示调用父类的空参构造方法

public class Test {
    public static void main(String[] args) {
        Fu f = new Fu();//定义一个父类的对象,//我是父类构造!
        Zi z = new Zi();//定义一个子类的对象,子类的构造方法可以调用父类的构造方法//我是父类构造!
        z.eat();//人需要吃饭
        z.getSum(3,5);//x+y=8
        z.fun();//静态方法推荐使用类名.调用//去玩
        Zi.fun();//去玩
        System.out.println(z.name);//大美
    }
}
public class Fu {
    int a = 9;//普通成员变量
    private int b = 20;//私有成员变量
    static int c = 100;//静态变量
    String name;
    //在父类当中定义方法
    public void eat(){
        System.out.println("人需要吃饭");
    }
    private void sleep(){
        System.out.println("人需要睡觉");
    }
    public static void fun(){
        System.out.println("去玩");
    }
    public int getSum(int x , int y){
        System.out.println("x+y="+(x+y));
        return x+y;
    }
    public Fu(){//父类构造方法
        System.out.println("我是父类构造!");
    }
    public Fu(String name){//父类有参构造
        this.name = name;
        }
    }
public class Zi extends Fu {
    //子类可以从父类中继承a=9 ,c两个成员变量
    int d = 200;
    int a = 900;//子类重新定义变量a,与继承来的变量a重名
    //子类从父类中继承到 eat()、getsum()、fun()方法
    @Override
    public int getSum(int x , int y ){
        System.out.println("(x+y)*2="+(x+y)*2);
        return (x+y)*2;
    }
    public Zi(){//子类中构造方法
        //第一行默认有一个,super(),调用父类对象的无参构造方法
        //父类优先于子类进内存,因为父类中的成员变量和方法,必须先进内存才能让子类使用
        //super("大美");//也可以调用父类有参构造
    }
}

继承中重写应用
public class Phone {
    public void call(){
        System.out.println("打电话!");
    }
    public void send(){
        System.out.println("发短信!");
    }
    public void show(){
        System.out.println("显示号码" );
    }
}
//定义一个新手机,使用老手机作为父类
public class NewPhone extends Phone {
    @Override
    public void show() {
        super.show(); //System.out.println("显示号码");把父类的show方法拿过来重复利用
        //子类再来添加更多内容
        System.out.println("显示姓名");
        System.out.println("显示头像");
    }
}
public class Demo01Phone {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.call();
        phone.send();
        phone.show();
        System.out.println("=================");
        NewPhone newPhone = new NewPhone();
        newPhone.call();
        newPhone.send();
        newPhone.show();
    }
}

8.super关键字的使用

  • super:关键字,表示父类对象的使用 
  1.  在子类的成员方法中,访问父类的成员变量
  2. 在子类的成员方法中,访问父类的成员方法
  3. 在子类的构造方法中,访问父类的构造方法super(实参); 
  •  this:关键字,表示子类对象的引用 
  1. 在本类的成员方法中,访问本类的成员变量 
  2. 在本类的成员方法中,访问本类的另一个成员方法
  3. 在本类的构造方法中,访问本类的另一个构造方法this(实参); 
  •  说明:super();或this();二者只能选择一个

public class Demo01Constructor {
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}
public class Fu {
    public Fu(){
        System.out.println("父类无参构造");
    }
    public Fu(int num){
        System.out.println("父类有参方法");
    }
}
public class Zi extends Fu {
    public Zi(){
        //super();//在调用父类无参构造方法  若不写 编译器赠送
        super(20);
        System.out.println("子类构造方法!");
    }
    public void method(){
        //super();//错误写法,只有子类构造方法才能调用父类构造方法
    }
}			

9.区分子类方法中重名的三种

  • 局部变量:直接写 
  • 本类的成员变量:this.成员变量名 
  • 父类的成员变量:super.成员变量名

public class Demo01ExtendsField {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method();
    }
}
public class Fu {
    int num = 10;
}
public class Zi extends  Fu{
    int num = 20;
    public void method(){
        int num = 30;
        System.out.println(num);//30 局部变量
        System.out.println(this.num);//20 本类的成员变量
        System.out.println(super.num);//10,父类的成员变量
    }
}	

10.说明:在父子类的继承关系中,如果成员变量重名,则创建子类对象时,有两种方式访问

* 直接通过子类对象访问成员变量:
*     等号左边是谁,就优先用谁,没有则向上找
* 间接通过成员方法访问成员变量:
*     该方法属于谁,该方法在哪定义的,没有则向上找
public class Demo01ExtendsField {
    public static void main(String[] args) {
        Fu fu = new Fu();
        System.out.println(fu.numFu);//只能用父类的东西,没有任何子类内容
        
        Zi zi = new Zi();
        System.out.println(zi.numFu);//子类没有,向上找父类中的
        System.out.println(zi.numzi);//子类有
        System.out.println("=================");
        //间接通过成员方法访问成员变量
        System.out.println(zi.num);//父子类发生重名,等号左边是谁,就优先用谁,优先子类
        zi.methodzi();//这个方法是子类的,优先用子类的,优先用子类的
        zi.methodFu();//这个方法是在父类当中定义的,属于父类 
    }
}

11.继承的特点

  • java语言是单继承的 一个类的直接父类只能有唯一一个 
  • java可以多级继承 
  • 一个子类的直接父类是唯一的,但是一个父类可以拥有多个子类。

双色球案例
    系统生成双色球:
        6颗红球(33选1) 1颗蓝球(16选1),要求6个红球号码不重复
    客户购买双色球:
        要求客户键盘输入,6个红球号码(1-33),1个蓝球号码(1-66)
    验证客户购买后是否中奖
    (1)6个红球和1个蓝球全部买中,一等奖, 1000万
    (2)买中1个蓝球,3-5个红球,二等奖,500万
    (3)买中一个蓝球,1-2个红球,三等奖,100万
    (4)买中一个蓝球,没买中红球,四等奖,50万
    (5)剩下情况没中奖
分析:
    1.系统生成,Random
    2.先自动生成一个蓝球,16选1 --nextInt(16)+1
    3.将6个红球放至数组中, --nextInt(33)+1
    4.使用循环,6个随机数放至int[6],数组中的元素不重复 一个int[33]索引
    5.客户通过键盘录入6个红球,范围1-33
    6.客户键盘录入1个蓝球,范围1-16
    7.验证中奖
        匹配系统6红球与客户输入的6个红球几个相同的,计数
        匹配系统1蓝球与客户输入的1个蓝球
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        //1.系统生成球
        Random r = new Random();
        int sysBlue = r.nextInt(16)+1;//生成1个蓝球
        int[] sysRed = new int[6];
        int[] arr = new int[33];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i+1;
        }
        for (int i = 0; i < sysRed.length; i++) {
            int count = r.nextInt(33)+1;
            if(arr[count-1] == 0)
                i--;
            else {
                arr[count-1] = 0;
                sysRed[i] = count;
            }
        }
        Scanner sc = new Scanner(System.in);
        int[] scRed = new int[6];
        for (int i = 0; i < scRed.length; i++) {
            System.out.println("请输入第"+(i+1)+"个红球编号:");
            scRed[i] = sc.nextInt();
            if(scRed[i]>33 || scRed[i]<1){
	        System.out.println("输入有误,请重新输入");
	        i--;
            }
        }
        System.out.println("红球输入完毕!");
        System.out.println("请输入1个蓝球:");
        int scBlue;
        while(true) {
            scBlue = sc.nextInt();
            if(scBlue>16 || scBlue<1){
	        System.out.println("蓝球输入有误,请重新输入:");
	    }else
                break;
	}
        System.out.println("蓝球输入完毕!");
        System.out.println("系统生成的红球为:"+ Arrays.toString(sysRed));
        System.out.println("系统生成的蓝球为:"+ sysBlue+'\n');
        System.out.println("您输入的红球为:"+ Arrays.toString(scRed)+'\n'+"蓝球为:"+scBlue);
        int count = 0;
        boolean flag = false;
        for (int i = 0; i < 6; i++) {
            if(sysRed[i] == scRed[i])
                count++;
        }
        if(sysBlue == scBlue)
            flag = true;
        if(flag == true) {//买中蓝球
            switch (count) {
                case 6:
                    System.out.println("恭喜你,一等奖:1000万!");
	            break;
	        case 5: case 4: case 3:
	            System.out.println("恭喜你,二等奖:500万!");
	            break;
	        case 2: case 1:
		    System.out.println("恭喜你,三等奖: 100万!");
	            break;
	        default:
	            System.out.println("恭喜你,四等奖:50万!");
	    }
        }else{
	    System.out.println("很抱歉,没有中奖");
	}
    }
}