封装,static,代码块,对象的打印

1. 封装

面向对象语言的三大特性是:封装,继承,多态。下面我们就来了解一下其中的封装

1.1 封装的概念

什么是封装?顾名思义,就是将一个东西用包装纸包起来,不让外界所看到。而在面向对象的编程语言中,封装的含义亦是如此,将一个类中的东西私密化,使只有类中才能使用它,类外则无法访问。封装的实际意义就是,将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

1.2 包的概念

在一个项目中,经常分给很多个人来分工完成,为了防止因为类名相同而冲突,因此引入了包,在不同的包,类名可以相同。包相当于电脑里的文件夹,给电脑里的文件进行分类。那么如何去创建一个包呢?

演示:
在这里插入图片描述
再输入包的名字,这样一个包就创建完成了


当我们在创建的一个包里创建一个Java文件时,最上面会自动出现一行代码
在这里插入图片描述
这就代表这个Java文件在这个包底下


当我们需要在一个包底下调用另一个包里的一个类时,就需要引入这个包下的类,这时就需要使用到import关键字
在这里插入图片描述

当我们点击方框时,编译器就会自动帮我们在这个Java文件的最上面加上一行代码
在这里插入图片描述
这就代表了引入这个包底下的这个类。引入时要么具体到类,要么在包路径最后加*(代表引入包里的所有类),更建议使用第一种,具体到类

1.3 访问修饰限定符

在类和对象一文中,我们经常使用到的public,其实就是一个访问修饰限定符。当然,访问修饰限定符不止public一个,还有private,default,protected。他们可以用来修饰变量、方法、类,来限定他们所能被访问的权限,权限如图:

在这里插入图片描述

其中,被private修饰的变量,只能在同一个包的同一个类中,才能被访问,这也就达到了封装的效果!

1.4 被封装的属性如何set和get?

通过前面,我们了解到,被封装的属性只能在类中才能去访问,如果想在类外进行访问,该怎么办呢?我们可以通过setter方法或者构造方法去给设置属性,可以通过getter方法取得属性

如何快速生成构造方法在上一篇文章已经讲解了,下面来演示如何快速生成getter和setter方法

第一步,按住alt + insert
在这里插入图片描述
第二步,选择是只生成setter,还是只生成getter,还是都生成!
在这里插入图片描述
第三步,选择需要生成方法的属性即可

生成结果:

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

2. static

在之前的C语言的学习中,我们也同样学习了static关键字,static就是静态的意思。被static修饰的变量、方法具有静态属性。当你想将一个属性/方法设置为这个类所公有的,而不是具体地属于某一个对象,这时就可以将其设置为静态的

2.1 再谈学生类

我们再以学生类为例,比如有三个学生对象,他们各有各的姓名、年龄,但是他们在同一个教室上课,而教室并不是某一个学生对象所特有的,而是公共的资源,这时就可以将教室这一属性设置为静态成员变量

public class Student {
    public String name;
    public int age;
    public static String classroom = "1班";//static修饰的成员变量

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void Sleep() {
        System.out.println("睡觉");
    }
}
public class Test3 {
    public static void main(String[] args) {
        Student student = new Student("zhangsan", 18);
        System.out.println(student.name + " " + student.age + " " + student.classroom);
    }
}

2.2 static修饰成员变量

被static修饰的成员不属于对象,而是属于类,因此又叫做类变量。类变量存储在方法区,而不是堆区,这也是为什么类变量属于类,而不属于对象。尽管既可以通过实例化对象来访问类变量,又可以通过类,又可以使用类名来直接访问类变量,但更推荐使用通过类名直接访问类变量!

下面来看一段代码,深入来理解类变量属于类,而不是对象

public class Student {
    public String name;
    public int age;
    public static String classroom = "1班";

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student = null;
        System.out.println(student.classroom);
        //System.out.println(student.name);//报错!
        //System.out.println(student.age);//报错!
    }
}

运行上述代码,可以发现,尽管student为null,但是依然可以使用student去访问classroom,而普通成员变量则不行!

这是因为,静态的成员变量是不依赖于对象的,因此,即使对象未实例化,或者为null,依然可以访问;但普通的成员变量依赖于对象,如果对象未实例化就去访问,就会出现”空指针“异常(NullPointerException)。

2.3 static修饰成员方法

static不仅可以用来修饰成员变量,也可以用来修饰成员方法,被static修饰的成员方法又叫做类方法,同样地,类方法不属于对象,而是属于类!

当我们将类变量封装起来,如果在类外想去访问时,就需要用到setter或者getter,我们用IDEA自动生成的代码来看看

public class Student {
    private String name;
    private int age;
    private static String classroom = "1班";

    public static String getClassroom() {
        return classroom;
    }

    public static void setClassroom(String classroom) {
        Student.classroom = classroom;
    }
}

不难发现,与普通成员变量生成的setter和getter有所不同,多了个static,这就是类方法!与类变量一样,类方法既可以通过对象的引用去访问,也可以通过类名直接去访问,更推荐第二种。

我们也可以把static去掉,这样也可以,如果这样,这就是非静态方法了,如果需要去访问这个方法时,就必须先要实例化这个对象,然后只能通过对象的引用去调用这个方法了!

注:

  • 静态方法中,不能直接使用非静态的成员变量和方法,也不能出现this
  • 而在非静态的方法中,可以直接调用静态方法和静态成员变量
  • 静态方法无法重写,无法用来实现多态

如果在静态方法中要使用非静态的成员变量和方法,必须先实例化对象,再通过对象的引用来访问
在这里插入图片描述
在这里插入图片描述

2.4 static成员变量初始化

静态成员的初始化可以通过两种方式:

  1. 就地初始化,即在定义静态成员时,就进行初始化
  2. 通过静态块进行初始化

3. 代码块

代码块是使用用{}定义的一段代码,代码块可分为:

  • 普通代码块
  • 构造代码块(实例代码块)
  • 静态代码块

3.1 普通代码块

普通代码块定义在方法中,例如:

public class Test1 {
    public static void main(String[] args) {
        {
            int a = 100;
            System.out.println("a = "+a);
        }
        int a = 200;
        System.out.println("a = "+a);
    }
}

main方法里的花括号里的就是一个普通代码块,有没有发现 “好像a被定义了两次” ,实际上并不是这样的,第一个a相对于第二个a来说就是局部变量,当出了花括号后,变量a就会被销毁,然后再去定义a,也就不会报错了,这可在一定程度上避免因重复定义同一个变量而报错的情况

本质上,定义在main方法里的代码都是普通代码块,而main方法里大括号里的时普通代码块里的普通代码块。

普通代码块用的比较少!

3.2 构造代码块(实例代码块)

构造代码块,有的书上也叫实例代码块,是定义在类中,方法外的代码块,一般用来初始化实例成员变量,例如:

在这里插入图片描述
注意普通代码块与实例代码块的区别:

  • 普通代码块是定义在方法中的
  • 而实例代码块是定义在类中,方法外的

3.3 静态代码块

静态代码块定义在类中,方法外,一般用来初始化静态的成员变量。当类被加载时,静态代码块就会被触发!

例如:

public class Student {
    private String name;
    private int age;
    private static String classroom;
    
    //静态块
    static {
        classroom = "1班";
    }
}

3.4 执行顺序

先看代码:

public class A {
    {
        System.out.println("A中的构造块");
    }
    public A() {
        System.out.println("A中的构造方法");
    }
    static {
        System.out.println("A中的静态块");
    }
}
public class Test {
    public Test() {
        System.out.println("Test中的构造方法!");
    }
    {
        System.out.println("Test中的构造块");
    }
    static {
        System.out.println("Test中的静态块");
    }
    public static void main(String[] args) {
        A a = new A();
    }
}

运行结果:
在这里插入图片描述
这说明其先后顺序为:静态块 -> 构造块 -> 构造方法,当主类中有静态块时,静态块会优先于main方法执行,因为静态块在Test类被加载时就会执行!

这时有人就会有疑问,为什么Test中的构造方法和构造块没有执行呢?这是因为,构造方法和构造块在创建对象时才会执行,且只执行一次,而main方法内并没有创建Test的对象,就不会调用构造方法和构造块了!

public class Test {
    public Test() {
        System.out.println("Test中的构造方法!");
    }
    {
        System.out.println("Test中的构造块");
    }
    static {
        System.out.println("Test中的静态块");
    }
    public static void main(String[] args) {
        Test test = new Test();
        A a = new A();
    }
}

如果实例化Test类,就会执行构造方法和构造块!

运行结果:
在这里插入图片描述

4. 对象的打印

先看代码:

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student = new Student("zhangsan", 18);
        System.out.println(student);
    }
}

运行结果:
在这里插入图片描述
我们知道,student是对象的引用,保存的是该对象的”地址“,因此,直接打印对象的引用得到就是一个”地址“

那么,如何去打印一个对象的具体属性呢?可以在Student中重写toString()方法
在这里插入图片描述
按住alt+insert,选择toString()
在这里插入图片描述
点击OK即可!

生成的代码:
在这里插入图片描述
当我们再次运行代码时,就会发现输出的不再是一个”地址“了,而是成员的具体属性。本质上就是输出toString()方法的返回值!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值