一. 代码块
概念:
使用{} 括起来的代码,称为代码块
分类:
根据它位置和声明的不同,我们可以将代码块分为局部代码块,构造代码块,静态代码块,同步代码块(多线程涉及)
a 局部代码块(方法内)
1)特点:
限定了变量的生命周期,变量在局部代码块中定义的,那么出了局部代码块之后,就访问不到了;在局部代码块中定义的变量,在出了代码块之后,内存就会释放掉
2)作用:
主要就是节约内存
局部代码块中定义的变量,虽然说作用域是局部的,但是如果存在外部嵌套的代码块,且在局部代码块定义之前就定义了某个变量,那么在我们局部的代码块中就不可以定义相同名称的变量;但是如果在局部代码块执行之后,去定义一个和局部代码块中相同名字的变量是可以的,因为局部代码块中的变量已经失去了作用域范围。
实例1:
public class Demo1{
public static void main(String[] args){
//int x=5;//此时会报错
{
int x=10;
}
int x = 20;
System.out.println(x);
}
}
b 构造代码块(方法外)
1)概念:
类中方法外出现,每次调用构造方法的时候,都会优先先调用构造代码块
2)特点:
每创建一个对象,都会调用一次我们的构造代码块(与创建对象伴生)
3)作用:
如果存在很多重载的构造方法,而且每个里面需要执行相同的逻辑,那么就可以考虑将这些代码提取到构造代码块中来执行;让我们代码结构更简练,增强了维护性。
4)补充:
使用场景其实不多见
c 静态代码块(伴随static)
1)概念:
类中方法外出现,但是用static 来进行修饰
2)特点:
随着类加载的时候执行
3)格式:
static{
//内容
}
用处:适用于整个软件的生命周期中,只需要执行一次的业务逻辑代码;比如我们之后数据库的操作
补充:
这几个代码块的优先顺数:静态代码块>构造代码块>局部代码块
实例2:
public class Demo2{
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("非默认构造");
// 数组成员变量我们依次赋值
}
}
编译结果:
原因:首次遇到main方法时会首次加载main方法所在的类Demo2,执行其静态方法(随类的加载而加载),然后返回到main方法中创建Person的对象,又因为创建对象首先要构造方法,而构造代码块优先于构造方法;静态代码块在所在类首次加载后不再加载静态代码块
二. 继承
特点:
1)子类继承父类,继承父类的成员变量和成员方法;但是他们各自拥有的各自的成员变量,所以他们的值并不会继承
2) 对于父类中的私有成员变量和私有方法,子类是无法继承的
格式:
子类 extends 父类
优点:
a.提高了代码的复用性
b.提高了代码的维护性
继承缺点:
类的耦合性增强了,破坏封装性
开发的原则:
高内聚,低耦合;通俗的讲:自己能完成的东西尽量自己完成不要依赖别人
耦合:类和类之间的关系
内聚:自己独立完成某件事情的能力
生活中的举例:
电视类: 品牌, 价格 , 尺寸
手机类: 品牌, 价格 , 尺寸, 操作系统
这两个类不能直接继承,但可以从上层抽出来一个类【共性】: 电器类(品牌,价格,尺寸)
实例3:
//主要目的:子类和父类各自拥有的各自的成员变量,所以他们的值并不会继承
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{
}
3 )只支持单继承,但是可以支持多层继承
继承是相互叠加的,子类继承的时候,会递归似的寻找父类中是否还存在继承,会继承整个层级直到最根部类的属性和方法
实例4:
/*
* 多层继承:
* Son类继承Father类,Father继承GrandFather类...甚至继承到根类
*/
public class ExtendsDemo1{
public static void main(String[] args){
GrandFather gFather = new GrandFather();
gFather.speak();
Father father = new Father();
father.speak();
father.eat();
Son son = new Son();
son.eat();
son.sleep();
son.speak();
}
}
class GrandFather{
void speak(){
System.out.println("祖父类speak");
}
}
class Father extends GrandFather{
void eat(){
System.out.println("父亲类eat");
}
}
class Son extends Father{
void sleep(){
System.out.println("儿子类sleep");
}
}
4)对于构造方法是无法继承的,但是有办法可以调用父类的构造方法
继承关系中访问成员变量:
a.不同名的成员变量
子类可以直接访问和使用父类继承的非私有的成员变量
b.同名的成员变量
优先原则: 如果子类中定义了和父类中相同名字的成员变量,会优先访问子类的该变量
如果想要依然访问到父类中的同名变量,我们需要加上super关键字来获取
this::当前对象的引用
super::当前父类对象的引用
this:可以访问到子类中的变量,也可以访问到父类中的变量
super::只能访问到父类中的变量,若父类没有则报错
实例5:
public class ExtendsDemo3 {
public static void main(String[] args) {
Son son = new Son();
son.print();
}
}
class Father {
int num1 = 10;
int num2 = 20;
}
// num1是子类中定义的一个和父类同名的变量
class Son extends Father {
int num1 = 40;
void print() {
// this:可以访问到子类中的变量,也可以访问到父类中的变量
// 说明:this.属性(如果子类没有此属性,则访问父类属性)
System.out.println(this.num1 + " .. " + this.num2);
System.out.println(super.num1 + " .. " + super.num2);
}
}
继承关系中访问成员方法:
a 不同名的方法:
子类可以直接调用到父类的非私有方法
b 同名的方法:
当父类和子类出现同名方法的时候(同名方法: 指的返回值类型(父子类关系是可以的),方法名和方法签名要一样)
在这种情况下:
当我们子类调用该方法时,会有优先原则的处理,就是会调用子类的该方法
方法的重写(覆盖):
存在于继承关系中,子类中定义的方法和父类中的方法完全一样的时候(返回值类型父子类关系是可以的),我们在通过子类对象来访问该方法的时候,就会调用到子类的方法
注意事项:
子类不能重写父类的私有方法
子类重写父类方法的时候,提供的访问权限不能更低
子类覆盖父类方法,如果父类是静态方法的话,子类也必须是静态方法才可以成功覆盖,也就是重写
方法重载:
同一个类中,如果我们有多个相同方法名的方法,但是他们的形参列表是不同的,那么这种方式我们就称为方法的重载
在调用的时候,jvm能够通过方法签名来区分到我们到底调用的是哪个方法
关于方法重写和方法重载的返回值约束:
方法重载:与返回值类型无关,必须参数列表不同
方法重写:子类与父类返回值类型(父子类关系是可以的) 必须相同
this:可以访问子类的方法,也可以访问父类的方法
super:只能够访问父类的方法
实例6:
/*
定义长方形类和正方形类, 长方形继承正方形并且重写计算面积和周长的方法.
*/
public class ClassTest1{
public static void main(String[] args){
Square square =new Square();
System.out.println("正方形周长"+square.getSumLength());
System.out.println("正方形面积"+square.getArea());
Rectangle rectangle = new Rectangle();
System.out.println("长方形周长"+rectangle.getSumLength());
System.out.println("长方形面积"+rectangle.getArea());
}
}
class Square{
private int lengthOfSide = 10;
int getSumLength(){
return 4*lengthOfSide;
}
int getArea(){
return lengthOfSide*lengthOfSide;
}
}
class Rectangle extends Square{
int width = 4;
int height = 5;
int getSumLength(){
return (width+height)*2;
}
int getArea(){
return width*height;
}
}
补充:
我们一个文件在编译之后产生的.class 文件的数量,取决于在该文件中定义的class的数量(非内部类),而且是不引入其他类的情况下
继承中构造方法的调用:
特点:
1 创建子类对象的时候,一定会优先去创建父类的对象;因为要创建父类对象,所以就需要去调用到父类的构造方法
2 对于我们所有的类的构造方法的第一行,第一行在我们没有自己添加this(...)或者super(...)的情况下都会去帮我们默认的添加super()
如果父类中不存在默认的构造,子类依然要创建对象,那么子类就需要显示的指明调用个父类哪个构造方法,才能保证父类对象创建的成功。
明确一点:
a 一定要保证父类对象的创建成功
b 构造方法的第一行,如果我们添加了自己调用的this(...),或者super(...),系统就不会为我们默认的添加super()
c 我们的this(...)或者super(...) 必须放到第一行,否则编译错误,且二者只能有其一
对c说明:this(...)调用的是子类的另一个构造方法,在此构造方法中调用父类super(参数),所以二者只需其一
通俗理解:子类要想创建对象,首先要创建父类对象,所以就需要调用父类的构造方法;当程序执行到new去创建子类对象的时候,首先会去调用子类的构造方法,若子类构造方法中没有his(...)或者super(...) ,jvm会默认添加super()调用父类的构造方法,进而完成父类对象的创建,然后再完成子类构造方法,进而创建子类对象
实例7:
/*
在创建子类对象的时候,会先调用到父类的构造方法,再去创建父类的对象
构造方法调用构造方法
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 在静态代码块中进行初始化
实例8:
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);
}
}
一些比较好的博客super详解文章:点击打开链接