1.封装的概念
面向对象的三大特性:封装、继承、多态。
什么是封装: 把类的细节进行隐藏,类外看不到这个数据。
2.访问限定符和包
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
这里说一下什么是同类、同包、不同包。
2.1那么包是什么呢?
①:自定义包
在java中包就是组织类的一种方式。
概念:为了更好的管理类,把多个类收集在一起成为一组,成为软件包。例如
文件夹src就是idea的默认包,包里面放了main类和test类。
包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式。
在同一个项目中可以存在相同名字的类,但是要在不同的包中 。例:
这样我们在src和demo01包下面都创建了Test类,编译器没有报错。
demo01属于我们的自定义包。
注意:
1. 在文件的最上方加上一个package语句指定这是在哪个包中。
2.包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式(com.baidu.demo)
3.如果一个类没有package语句,则该类被放到默认包中。
4.包名要和代码路劲相匹配。
我们创建了一个com.xxx。code的包,在下面创建了一个test类,则在文件夹中可以找到,
②:import导入包
java中提供了很多的类给我们使用,例如:
我们通过java.util来导入里面的Date包,在通过创建的对象date来调用包里面的getTime类
就可以获得一个时间戳。
这样写是不是很麻烦呢?
我们还可以在最上面来使用import来导入包。
这样我们在前面就不需要再写java.util了。
我们如果还要使用里面的Arrays里面的类怎么办呢?再写一个import java.util.Arrays吗
其实不用,我们可以写成java.util.* ,
*可以认为是通类符,可以充当包里的任何类。并不是说使用了*就是把包里面的类都导入了,而是使用哪个类导入哪个类。
这里要注意的是:一个类不一定只有一个,他可以再不同的包底下。例如Date
再util下有,再sql下面也有,如果我们使用了*,那么使用哪一个呢,编译器就搞不清楚了,就会报错
所以非必要情况我们一般不使用*。
还有一种是import static导入静态的包。
上面可以看出Math出现了多次,我们可以使用import static java.lang.Math.*
这样我们就不用一直写Math了。
2.2 修饰限定符
上面也看到了这张图,那么我们也已经讲过了包的概念,下面我们看看什么是同包不同类和不同包和同类
①:同类 private。
class Student {
private String name;
private int age;
public void attendClass() {
System.out.println(this.name + "再上课");
}
public static void main(String[] args) {
Student student01 = new Student();
student01.name = "zhangsan";
}
}
public class Test01 {
Student student01 = new Student();
student01.name = "zhangsan"
}
根据代码块和上图可以看出,我们在Studeng类中创建了一个main方法,在main方法创建了一个对象
然后使用变量名 + . 来访问对象的属性。
可以成功生成,但是在test01类中却无法访问,这是因为成员变量属性被private修饰了,只能在同类中使用。
如果我们要在类外使用的话可以给他创建一个公开的接口。如:
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;
}
一个get接收属性,一个set设置属性,我们在类外调用这些方法即可。
可以看出我们调用set方法后成功的给类外的对象进行了赋值。
这就是private,只能在同类中调用,在不同类调用需要创建一个公共接口来给其他人使用。
②:同包 default
我们在Test2这个类中创建了一个默认变量age,根据图可以看出在同一个包中我们创建的对象可以正常访问,但是在不同的包下面无法访问。
这里要注意一点: 要使用不同包的类,要先使用import将包导进来。
③: 不同的包 public
这里就比较简单了,就是我们经常使用的public,表示就算是不同的包,不同的类也可以访问,相当于万
能钥匙,所有门都可以开。
3.static修饰的成员变量和成员方法
3.1:static修饰成员变量
我们继续来定义一个学生类变量。
class Student {
public String name;
public int age;
public String classRoom;
public Student(String name, int age, String classRoom) {
this.name = name;
this.age = age;
this.classRoom = classRoom;
}
public void printStudent() {
System.out.println("姓名: " + this.name + " 年龄: " + this.age + " 班级: " + this.classRoom);
}
}
public class Test01 {
public static void main(String[] args) {
Student student = new Student("zhangsan", 18, "108");
student.printStudent();
Student student1 = new Student("lisi",19,"108");
student1.printStudent();
}
}
上面我们可以看出,班级都可以是一样的,我们能不能不输入班级,让他一直保持是108呢?
接下来就得用到我们的static了。比如:
我们将classRoom用static修饰之后,我们将他直接初始化,后面我们创建的对象不用给他赋值也可以使用。
static修饰的成员变量,称为静态成员变量,也可以称为类成员变量。
特性:
不属于某个具体的对象,是所有对象所共享的,不存储在某个对象的空间中。
既可以通过对象访问,也可以通过类名访问,一般通过类名访问。
类变量存储在方法区中。
生命周期伴随类的一生。
3.2 staatic修饰成员方法
一般来说,类的成员变量都是通过private修饰的,那么在加上static之后,我们要如何创建它的接口呢?
通过idea我们生成了两个获取和修改被static修饰后的类变量的方法。
那么被static修饰的方法有什么特性呢?
与类成员一样,不属于某个具体的对象,属于类方法。
可以通过对象调用,也能通过类名调用。
不能再静态方法中访问任何非静态成员变量。(因为静态方法不依赖对象,不创建对象也可以使用,但是没被static修饰的变量却不能,所以不能放在类方法里面)
静态方法中不能调用任何非静态方法(静态方法中有隐藏的this函数,再静态方法调用的时候无法调用this引用)
3.3static的初始化
①:就地初始化
即在创建的时候就给它赋值。例:
②:静态代码块初始化
那啥是代码块呢? 我知道你很急,但你先别急😂😂😂。马上见分晓
4.代码块
4.1概念
用{}定义的一段代码称为代码块。
4.2普通代码块
用{}直接定义,普通方法块
可以看到普通代码块也要遵循局部优先原则。
普通代码块的成员变量在外部访问不到。
4.3构造代码块
class Student {
private String name;
private int age;
private static String classRoom = "108";
{
this.name = "张三";
this.age = 18;
System.out.println("构造代码块");
}
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;
}
public static String getClassRoom() {
return classRoom;
}
public static void setClassRoom(String classRoom) {
Student.classRoom = classRoom;
}
定义在类内部的代码块称为构造代码块。也称为实例代码块。主要作用是用来初始化成员变量。
4.4静态代码块
使用static修饰的代码块称为静态代码块,主要用来初始化静态成员变量。
4.5代码块间的执行顺序
class Student {
private String name;
private int age;
private static String classRoom;
{
this.name = "张三";
this.age = 18;
System.out.println("构造代码块");
}
static {
Student.classRoom = "108";
System.out.println("静态代码块");
}
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;
}
public static String getClassRoom() {
return classRoom;
}
public static void setClassRoom(String classRoom) {
Student.classRoom = classRoom;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法");
}
public void printStudent() {
System.out.println("姓名: " + this.name + " 年龄: " + this.age + " 班级: " + Student.classRoom);
}
}
public class Test01 {
public static void main(String[] args) {
Student student = new Student("zhangsan", 18);
student.printStudent();
Student student1 = new Student("lisi",19);
student.printStudent();
}
}
根据上面代码的运行结果我们可以得出,静态代码块是最先调用的,并且创建对象只会调用一次
其次是构造代码块,最后才是构造方法。我们在代码块中初始化的变量,如果在构造方法中给它赋值时,
初始化的变量便会改变。下面我们再看一下不给赋值的结果。
当我们使用不带参数的构造方法时,便会按我们初始化的数值来输出。
当我们有两个静态代码块呢?
根据上图我们可以得到静态代码块会按照顺序来进行执行,并且也只会执行一次。
注意:
静态代码块不管生成多少对象,都只会执行一次
静态成员变量是类的属性,因此实在jvm加载类时开辟空间并初始化的
如果一个类中有多个静态代码块,再运行时,会按照顺序依次执行
构造块只有在创建对象时才会执行
5.对象的打印
class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public void print() {
System.out.println(this.name);
System.out.println(this.gender);
System.out.println(this.age);
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("lisi","男",18);
person.print();
}
}
正常来说我们要输出对象里的成员属性,需要自己去创建一个print方法。
那么我们来看一下println方法是怎么实现的呢?
这里可以看出,println最后调用的是toString方法,那么我们是否可以通过重写toString方法来实现直接输出呢?
可以看到我们最后还是正常的输出了。
这就是对象的打印。