day09
- 代码块
1.1 代码块的概念
代码块就表示一对大括号,但是定义的位置不同,功能就不同{逻辑}
代码块的分类:
局部代码块
构造代码块
静态代码块
同步代码块(多线程环节,讲解用于解决线程安全问题)
1.2 局部代码块
格式 : {逻辑}
位置 : 定义在方法中
作用 : 限制定义在局部代码块中的变量的作用范围
注意: 1) 定义在局部代码块之外的变量,那么在代码块中对变量做的操作,出了局部代码块,仍然生效;
2) 定义在局部代码块中的变量,一个是作用范围有限,如果局部代码块执行完毕,那么局部代码块中定义的变量就会变成垃圾,就会被回收掉,释放内存,节省内存空间.
代码
// 局部代码块 : {} 定义在方法中
public class DaiMaKuai1 {
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);// 报错,j的使用范围有限
}
}
1.3 构造代码块
格式 : {逻辑}
位置 : 类中方法外
作用 : 给成员变量进行赋值,如果多个构造方法都有一部分相同的逻辑,那么这部分相同的逻辑可以写在构造代码块中
执行机制 : 构造代码块,在创建对象的同时,JVM主动的调用执行,在构造方法之前执行,每次创建一个对象,构造代码块只执行一次
代码
//构造代码块
public class DaiMaKuai2 {
private String name ;
// 定义一个构造代码块
{
name = “张三”;
System.out.println(“构造方法被调用了”);
}
public DaiMaKuai2(){
//System.out.println(“构造方法被调用了”);
}
public DaiMaKuai2(String name){
//System.out.println(“构造方法被调用了”);
this.name = name;
}
public String getName(){
return name;
}
public static void main(String[] args) {
DaiMaKuai2 d2 = new DaiMaKuai2("QQ");
System.out.println(d2.getName());// QQ
}
}
1.4 静态代码块
格式 :
static {
逻辑
}
位置 : 类中方法外
作用 : 给静态的成员变量进行赋值,如果一个功能仅仅在类加载进内存时,只执行一次,那么这个部分功能可以写在静态代码块中
执行机制 : 当.class文件进入到内存中时,JVM会主动的调用一次静态的代码块,静态代码块在整个类的执行过程中,只执行一次,与创建多少对象无关
总结代码块的执行顺序:
静态代码块(只执行一次,与创建几次对象无关)----->构造代码块(每次创建对象执行一次)---->构造方法(每次创建对象执行一次) 局部代码块(方法进栈运行执行)
代码
// 静态代码块
public class DaiMaKuai3{
static String name ;
// 定义一个静态代码块
static{
name = “大美”;
System.out.println(“静态代码块执行了”);
}
// 构造代码块
{
System.out.println(“构造代码块begin”);
}
public DaiMaKuai3(){
System.out.println("构造方法执行了");
}
public static void main(String[] args) {
DaiMaKuai3 d3 = new DaiMaKuai3();
DaiMaKuai3 dd3 = new DaiMaKuai3();
System.out.println(DaiMaKuai3.name);// 大美
}
}
执行结果:
- 继承
面向对象的一大特征
2.1 继承的概述
继承 : 指类与类之间的关系,子父关系
关键字 : extends ,扩展,继承
举例 : class A{} class B{} ----> class A extends B{}----> A类继承B类
B类 : 叫做父类,超类,基类
A类 : 叫做子类,派生类
2.2 继承发生的场景
继承,多个事物具有了一些共性,可以将共性向上抽取,抽取到一个父类中,其他类作为这个父类的子类存在,在父类中实现共性,子类中不需要再重复实现共性内容,可以直接使用父类中内容
一个Cat,猫类
class Cat{
睡觉;
吃饭;
抓老鼠;
}
一个类Dag,狗类
class Dog{
睡觉;
吃饭;
看家;
}
猫和狗都是属于动物,发现了动物具有共性,睡觉和吃饭,于是将睡觉和吃饭功能向上抽取,抽取到一个父类中,父类称为Animal,动物类
class Animal{
睡觉;
吃饭;
}
class Cat extends Animal{
抓老鼠;
}
class Dog extends Animal{
看家;
}
说明 : 请大家不要因为几个方法就去使用继承关系
代码
// 猫属于动物,因此猫继承动物类,作为动物类的子类
public class Cat extends Animal{
/*public void sleep(){
System.out.println("睡觉");
}
public void eat(){
System.out.println("吃饭");
}*/
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
// 狗也是动物的一种,因此狗继承动物类,作为动物类的子类
public class Dog extends Animal{
/*public void sleep(){
System.out.println(“睡觉”);
}
public void eat(){
System.out.println("吃饭");
}*/
public void lookHome(){
System.out.println("狗能看家");
}
}
// Animal动物类 : 猫属于动物,狗也属于动物
public class Animal{
// 动物都需要吃饭和睡觉
public void sleep(){
System.out.println(“睡觉”);
}
public void eat(){
System.out.println(“吃饭”);
}
}
// 测试动物类
public class TestAnimal {
public static void main(String[] args) {
Cat c = new Cat();
c.sleep();// sleep方法来自于父类Animal中方法
c.eat();// eat来自父类Animal中的方法
c.catchMouse();// 猫类中自有方法
Dog d = new Dog();
d.sleep();
d.eat();
d.lookHome();
}
}
2.3 继承的优势和弊端
继承的优势:
- 提高代码的复用性
- 提高代码的可维护性(代码维护:代码修改的难易程度),表示代码修改时,方便,简易
- 继承为多态提供了前提
继承的弊端:
- 提高了类与类之间的耦合性
耦合性 : 指类与类之间的关系,太紧密了
举例,上面猫个狗,动物案例
cat类和dog类中的sleep和eat方法,都是来自于Animal类中
如果将Animal类删除了,Animal中所有子类所使用的sleep和eat都会受到影响
实际开发场景:
需要大家尽量做到类: 高内聚,低耦合
解释 : 类中的功能与其他外类的关联尽量不要太多,多数的功能能在类本身中实现
2.4 继承中的注意事项
- 父类中的私有成员(成员变量,方法),子类不能继承使用
原因 : private修饰的私有成员变量和方法,只能在本类中使用,子类并不是父类本身,也属于外类,因此私有无法被子类继承和使用
- 父类中的构造方法,不能被子类继承
原因 : 父类中的构造方法,与父类的名字一致;子类中的构造方法,与子类名字一致.弗雷德构造方法的名字与子类的名字不同,二构造方法的名字需要与类名一致,矛盾(父类构造不能被子类继承,但是可以被子类的构造方法调用)
代码
// 定义一个父类
public class Fu {
private int i = 9 ;
private void fun(){
System.out.println("去玩");
}
public void work(){
System.out.println("工作");
}
}
// 定义一个子类,作为Fu类的子类
public class Zi extends Fu {
}
public class TestFu{
public static void main(String[] args) {
Zi z = new Zi();
// 子类不能继承和使用父类中的私有成员
//System.out.println(z.i);
// z.fun();
z.work();
}
}
2.5 成员变量在继承中的关系
子类可以无条件的继承父类中的所有非私有成员变量
- 子类可以使用父类中所有的非私有成员变量; 父类只能使用类中定义的成员变量,无法使用子类自己定义的成员变量
- 变量的访问具有就近原则,使用变量时,哪个变量离的最近,优先使用哪个变量(变量自己有不找别人)
- 如果方法中定义了局部变量,优先使用局部变量
- 如果没有局部变量,优先使用类中定义的成员变量
- 如果类中没有成员变量,优先使用父类中成员变量
- 父类中如果也没有成员变量,那么接着继续找父类的父类…直到找到Object类,仍然没有这个变量,报错
代码
// 定义一个父类
public class Fu {
// 在父类中定义成员变量 : 数据类型 变量名 = 变量值;
int a = 9 ;// 普通成员变量
private int b = 20;// 私有成员变量
static int c = 100;// 静态成员变量
}
// 定义一个子类,作为Fu类的子类
public class Zi extends Fu {
// Zi类可以从Fu类中继承到两个成员变量 int a = 9,c
int d = 200;
// 子类重新定义了变量a,与从父类继承来的变量a重名
int a = 900;
}
public class TestFu{
public static void main(String[] args) {
// 定义一个父类的对象
Fu f = new Fu();
// 父类不能使用子类自己定义的变量
//System.out.println(f.d);
// 定义一个子类的对象
Zi z = new Zi();
// 使用了子类重新定义的变量a,根据变量的就近访问原则,子类自己有的
// 变量,就不找父类调用
System.out.println(z.a);// 900
// 使用从父类继承而来的变量
System.out.println(z.c);// 100
// 使用从父类继承而来的变量
System.out.println(z.d);// 200
}
}
2.6 方法在继承中的关系
子类可以无条件的继承父类中的所有非私有的方法(构造方法除外)
- 子类中可以重写(Override)从父类中继承来的方法
- 什么叫方法的重写? 发生在子父类的继承关系中,子类将从父类继承来的方法重新定义一遍
- 方法重写,必须与方法名相同,参数列表相同,返回值相同
- 子类的方法权限,要大于等于父类方法权限
public -----> 公共的,最大权限(到处都可以使用)
默认权限----> 定义一个方法或者变量,没有给任何的修饰符,那么就是默认修饰,也是默认权限,使用default表示,但是default不能写出来,写出来报错
private----> 私有的,最小权限(只能在本类中使用)
public -----> 默认----->private
4) 子类重写了父类的方法后,调用的就是子类的重写方法
5) 证明方法是重写方法,使用注解 @Override 写在方法之上,注解能帮你校验这个方式是不是重写的方法
6) 静态方法,只能继承使用,不能重写
因为静态方法,属于类;
代码
// 定义一个父类
public class Fu {
//在父类中定义方法
public void eat(){
System.out.println("人需要吃饭");
}
public int getSum(int x , int y){
System.out.println("x + y = " + (x+y));
return x + y ;
}
private void sleep(){
System.out.println("人需要睡觉");
}
public static void fun(){
System.out.println("去玩");
}
}
// 定义一个子类,作为Fu类的子类
public class Zi extends Fu {
// Zi类可以从Fu父类中继承到 eat , getSum ,fun
/*
public int getSum(int x , int y){
System.out.println("x + y = " + (x+y));
return x + y ;
}
*/
@Override
public int getSum(int x , int y){
System.out.println("(x + y)*2 = " + (x+y)*2);
return (x+y)*2;
}
/*
报错,不是方法重写
@Override
void fun1(){}*/
// 静态方法不能重写,可以继承使用
public static void fun(){
System.out.println("去玩");
}
}
public class TestFu{
public static void main(String[] args) {
// 继承之间的方法
// 调用从父类继承的方法eat
z.eat();
// 调用子类的重写方法
z.getSum(3,5);
// 调用从父类继承的方法fun
z.fun();
// 调用从父类继承的方法fun
Zi.fun();
}
}
说明 : 什么叫方法的重载(Overload)?什么叫做方法的重写(Override)?
重载: 在同一个类中,方法名相同,参数列表不同,与方法的返回值类型无关
重写: 在子父类的继承关系中,子类重写父类中的方法,要求 : 方法名,参数列表,返回值类型都必须与父类保持一致,重写的方法权限要大于等于父类权限
2.7 构造方法在继承中的关系
子类无法继承父类中的构造方法,但是子类的构造方法可以调用父类的构造方法
- 子类构造方法方法的第一行,默认存在(不写也存在)有一个表达式,super()
super : 表示父类对象的引用
super() : 表示调用父类的空参数构造方法
子类构造方法第一行也可以手动的调用父类的有参数构造 super(实际参数);
代码
public class Fu1 {
String name ;
public Fu1(){
System.out.println(“我是父类构造”);
}
public Fu1(String name){
this.name = name;
}
}
public class Zi1 extends Fu1{
public Zi1(){
// super()表示调用父类的空参数构造方法,默认存在
// 为什么 ? 构造方法是在new对象时,JVM虚拟机默认调用
// 如果需要调用父类的构造方法,父类的构造存在于父类中,于是
// 父类就需要进内存,父类优先于子类进内存,因为父类中的成员变量和方法
// 必须要先进内存,为了保证让子类继承使用
//super(); 默认
super(“大美”);// 可以手动在构造方法第一行调用父类的有参数构造
}
public static void main(String[] args) {
Zi1 zi = new Zi1();// 我是父类构造
System.out.println(zi.name);// 大美
}
}
2.8 super关键字的使用
super : 关键字,表示父类对象的引用
- 区分子类和父类重名的成员变量,加有super. 变量就是调用父类的成员变量(同理方法使用)
- 在子父类继承关系中,子类构造方法的第一行,默认调用super(); 也可以手动的添加其他的父类构造方法,super() ,必须写在构造方法第一行
this : 关键字,表示子类对象的引用
- 区分成员变量和局部变量的重名问题,加有this.关键字的变量就是成员变量
- 同一个类中,构造方法互相调用,使用this(实际参数);必须写在构造方法第一行
说明 : super() 或者 this() 二者只能选择一个
代码
public class Fu1 {
String name = “Fu”;
public Fu1(){
System.out.println(“我是父类构造”);
}
public Fu1(String name){
this.name = name;
}
}
public class Zi1 extends Fu1{
int age ;
String name = “Zi”;
public Zi1(){
//super(); 默认
}
public Zi1(int age){
// super() 或者 this() 二者只能选择一个
//super(“大美”);
this();
}
public void getName(){
//现在想要Fu结果,super----> new Fu()
System.out.println(super.name);//Fu
}
public static void main(String[] args) {
Zi1 zi = new Zi1();// 我是父类构造
zi.getName();//Fu
Zi1 z = new Zi1(12);// 我是父类构造
System.out.println(z.name);//zi
}
}
2.9 继承的特点
-
类与类之间只能单继承,一个子类只能有一个直接的父类
举例 : class A{} class B{} class C{}
class A extends B{}
classA extends C{}
-
Java中支持多层继承
class A{}
class B{
public void fun(){}
}
class C{
public void eat(){}
}
class A extends B{// A类是B类的子类
}
class B extends C{// B类是C类子类
}
说明 : B类是A类的父亲,C类是A类爷爷,
A类同时具有B类和C类的所有可继承的成员,A类即可以使用fun方法,也可以使用eat方法