一.代码块
概念:
使用{} 括起来的代码,称为代码块
分类:
根据它位置和声明的不同,我们可以将代码块分为局部代码块,构造代码块,静态代码块,同步代码块(多线程涉及)
a. 局部代码块
限定了变量的生命周期,变量在局部代码块中定义的,那么出来局部代码块之后,就访问不到了。在局部代码块中定义的变量,
在出了代码块之后,内存就会释放掉。
作用: 主要就是节约内存.
局部代码块中定义的变量,虽然说作用域是局部的,但是如果存在外部嵌套的代码块,且在局部代码块定义之前就定义了某个变量,
那么在我们局部的代码块中就不可以定义相同名称的变量。
但是如果在局部代码块执行之后,去定义一个和局部代码块中相同名称的变量,是可以的。因为局部代码块中的变量已经失去了作用域范围。
b.构造代码块
概念:
类中方法外出现,每次调用构造方法的时候,都会优先先调用构造代码块。
特点:
没创建一个对象,都会调用一次我们的构造代码块.
作用:
如果存在很多重载的构造方法,而且每个里面需要执行 相同的逻辑,那么就可以考虑将这些代码提取到构造代码块中来执行。
让我们代码结构更简练。增强了维护性.
使用场景其实不太多见。
c.静态代码块
概念:
类中方法外出现,但是用static 来进行修饰。
特点:
随着类加载的时候执行。
用处:
适用于整个软件的生命周期中,只需要执行一次的业务逻辑代码。
比如我们之后数据库的操作.
static{
System.out.println("demo2 静态代码块");
}
public static void main(String[] args){
Person person = new Person();
System.out.println("--------------------");
Person person1 = new Person(20);
}
}
class Person{
int age;
int[] num;
static{
System.out.println("调用了静态代码块");
}
{
//构造代码块
System.out.println("构造代码块");
}
//默认的构造方法
Person(){
System.out.println("默认构造");
// 数组成员变量我们依次赋值
}
Person(int age){
this.age = age;
System.out.println("非默认构造");
// 数组成员变量我们依次赋值
}
}
打印结果
二.继承
特点
1. 子类继承父类,继承父类的成员变量和成员方法。但是他们各自拥有的各自的成员变量,所以他们的值,并不会继承。
2. 对于父类中的私有成员变量和私有方法,子类是无法继承的。
继承优点:
a.提高了代码的复用性
b.提高了代码的维护性
继承缺点:
a.类的耦合性增强了。
开发的原则:
高内聚,低耦合.
耦合: 类和类之间的关系
内聚: 自己独立完成某件事情的能力.
电视类: 品牌, 价格 , 尺寸
手机类: 品牌, 价格 , 尺寸, 操作系统
上层抽出来一个类: 电器类(品牌,价格,尺寸)
3.只支持单继承,但是可以支持多层继承
继承是相互叠加的,子类继承的时候,会递归似的寻找父类中是否还存在继承,会继承整个层级直到最根部类的属性和方法。
4.对于构造方法是无法继承的。
但是有办法可以调用父类的构造方法。
继承关系中访问成员变量:
a.不同名的成员变量
子类可以直接访问和使用父类继承的非私有的成员变量
b.同名的成员变量
优先原则: 如果子类中定义了和父类中相同名字的成员变量,会优先访问子类的该变量
如果想要依然访问到父类中的同名变量,我们需要加上super关键字来获取.
this: 当前对象的引用
super: 父类对象的引用
this: 可以访问到子类中的变量,也可以访问到父类中的变量
super: 访问父类中的变量
继承关系中访问成员方法:
a.不同名的方法
子类可以直接调用到父类的方法
b.同名的方法
当父类和子类出现同名方法的时候(同名方法: 指的返回值类型(父子类关系是可以的),方法名称以及方法接收的形参列表要一模一样)
在这种情况下:
当我们子类调用该方法时,会有优先原则的处理,就是会调用子类的该方法。
public class ExtendsDemo1{
public static void main(String[] args){
Father father = new Father();
father.setName("小诸葛");//外部方法赋值
Son son = new Son();
/*
* 子类和父类各自拥有的各自的成员变量,所以他们的值并不会继承
* 可以这样理解:父类创建一个对象后给对象属性赋值
* 此时子类继承父类,但是此时子类继承的是父类的原始属性和方法
* (可以理解为未创建对象之前的属性和方法)
*
//主要目的:子类和父类各自拥有的各自的成员变量,所以他们的值并不会继承
public class ExtendsDemo1{
public static void main(String[] args){
Father father = new Father();
father.setName("小诸葛");//外部方法赋值
Son son = new Son();
/*
* 子类和父类各自拥有的各自的成员变量,所以他们的值并不会继承
* 可以这样理解:父类创建一个对象后给对象属性赋值
* 此时子类继承父类,但是此时子类继承的是父类的原始属性和方法
* (可以理解为未创建对象之前的属性和方法)
*/
son.speak();
}
}
class Father{
//JavaBean类
private int age;
private String name;
/*
* 若在此直接赋值,则子类也继承此值
* 即:private String name="小花";
* 若修改成private static String name;
* 调用外部方法对父类赋值后子类仍然能继承此值
*/
void setAge(int age){
this.age = age;
}
void setName(String name){
this.name = name;
}
int getAge(){
return age;
}
String getName(){
return name;
}
void speak(){
System.out.println("爸爸大声喊出我的名字:"+name);
}
}
class Son extends Father{
}
补充:
1.
方法的重写(覆盖):
存在于继承关系中,子类中定义的方法和父类中的方法完全一样的时候(返回值类型父子类关系是可以的),我们在通过子类对象来访问该方法的时候,就会调用到子类的方法
注意事项:
子类不能重写父类的私有方法
子类重写父类方法的时候,提供的访问权限不能更低
子类覆盖父类方法,如果父类是静态方法的话,子类也必须是静态方法才可以成功覆盖,也就是重写
方法重载:
同一个类中,如果我们有多个相同方法名的方法,但是他们的形参列表是不同的,那么这种方式我们就称为方法的重载
在调用的时候,jvm能够通过方法签名来区分到我们到底调用的是哪个方法
关于方法重写和方法重载的返回值约束:
方法重载:与返回值类型无关,必须参数列表不同
方法重写:子类与父类返回值类型(父子类关系是可以的) 必须相同
this:可以访问子类的方法,也可以访问父类的方法
super:只能够访问父类的方法
2.
我们一个文件在编译之后产生的.class 文件的数量,取决于在该文件中定义的class的数量(非内部类),而且是不引入其他类的情况下
继承中构造方法的调用:
特点:
1 创建子类对象的时候,一定会优先去创建父类的对象;因为要创建父类对象,所以就需要去调用到父类的构造方法
2 对于我们所有的类的构造方法的第一行,第一行在我们没有自己添加this(…)或者super(…)的情况下都会去帮我们默认的添加super()
如果父类中不存在默认的构造,子类依然要创建对象,那么子类就需要显示的指明调用个父类哪个构造方法,才能保证父类对象创建的成功。
明确一点:
a 一定要保证父类对象的创建成功
b 构造方法的第一行,如果我们添加了自己调用的this(…),或者super(…),系统就不会为我们默认的添加super()
c 我们的this(…)或者super(…) 必须放到第一行,否则编译错误,且二者只能有其一
对c说明:this(…)调用的是子类的另一个构造方法,在此构造方法中调用父类super(参数),所以二者只需其一
通俗理解:子类要想创建对象,首先要创建父类对象,所以就需要调用父类的构造方法;当程序执行到new去创建子类对象的时候,首先会去调用子类的构造方法,若子类构造方法中没有his(…)或者super(…) ,jvm会默认添加super()调用父类的构造方法,进而完成父类对象的创建,然后再完成子类构造方法,进而创建子类对象.
/*
在创建子类对象的时候,会先调用到父类的构造方法,再去创建父类的对象
构造方法调用构造方法
this(参数)---调用同类
super(参数)--调用父类
*/
public class ExtendsDemo5 {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father {
Father() {
System.out.println("调用father 默认构造");
System.out.println("father对象创建完毕");
}
Father(int num) {
System.out.println("调用father 有参构造");
System.out.println("father对象创建完毕");
}
}
class Son extends Father {
Son() {
this(10);// 不要此行的编译结果
// this(10)调用同类的构造方法,此时就不会帮我们再去默认添加super(),
System.out.println("son 默认构造");
}
Son(int num) {
super(10);// 可以删除此行看看编译结果
// 第一行没有this或者super,就会帮我们默认添加super()
System.out.println("son 有参构造");
System.out.println("-------------");
}
}
三.final关键字
a final修饰类
final如果修饰一个类,该类不能被继承
b final修饰类的成员变量
该成员变量的值不可修改
问题: 那么什么时候初始化这个值?
答:在对象创建之前都可以
a 构造代码块中可以进行初始化
b 构造方法中可以进行初始化
c 定义成员变量的时候,可以直接进行初始化
注意一点:
1)这三种方式同时只能用一种
2)在大多数的场景下,我们的final成员变量都会配合public static 一起使用
//final修饰的常量类,变量命名都要大写,而且单词之间要用_分割开
形如:
class A{
public static final double PI = 3.14;
public static final double RATE = 2.86;
}
使用方式:类名.变量名
A.PI
A.RATE
final修饰的static变量的初始化时机:
a 在创建静态final成员变量的时候,直接进行初始化
b 在静态代码块中进行初始化
public class FinalDemo1 {
//final修饰的static变量初始化的一种方式
public static void main(String[] args) {
Person person = new Person();
person.sayNum();
}
}
class Person {
// final 修饰成员变量,该值不可修改.
static final int num;//第二种方式,定义的时候直接赋值
static {
num = 10; //第一种方式:通过静态代码块赋值
}
Person() {
}
void sayNum() {
System.out.println("静态变量初始化:" + num);
}
}