封装,static,代码块,对象的打印
- 1. 封装
- 1.1 封装的概念
- 1.2 包的概念
- 1.3 访问修饰限定符
- 1.4 被封装的属性如何set和get?
- 2. static
- 2.1 再谈学生类
- 2.2 static修饰成员变量
- 2.3 static修饰成员方法
- 2.4 static成员变量初始化
- 3. 代码块
- 3.1 普通代码块
- 3.2 构造代码块(实例代码块)
- 3.3 静态代码块
- 3.4 执行顺序
- 4. 对象的打印
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成员变量初始化
静态成员的初始化可以通过两种方式:
- 就地初始化,即在定义静态成员时,就进行初始化
- 通过静态块进行初始化
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()方法的返回值!