文章目录
六、封装
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
在Java中主要通过类和访问权限实现封装,类可以将数据和方法结合在一起,而访问权限用来控制方法或者字段能否直接在类外使用;访问权限可以限定类中成员的可见性,也可以控制类的可见性。
Java中的四种访问限定符:public、protected、default、private
public:都可以看到
protected:主要在继承中使用
default(默认权限):同一个包中的都知道,对于外人就是隐私
private:只有自己知道,其他人都不知道
一般情况下,成员变量设置为 private,成员方法设置为 public
修饰类(外部类)只能使用public或默认
包
为了更好的管理类,把多个收集在一起成为一组,称为软件包
包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式;
在同一个工程中允许存在两个相同的类,只要处在不同的包中就可以
导入包中的类
方法一
public class TestDate {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
//得到一个毫秒级别的时间戳;如果想看到正常的时间还需要格式化
System.out.println(date.getTime());
}
}
优点:不会发生冲突
缺点:麻烦
运行结果
1649238358096
方法二
//使用import语句导入包
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getTime());
}
}
如果还需要使用 java.util 中其他的类,可以使用
import java.util.*
优点:使用较为简单
缺点:可能会发生冲突
比如:在 import java.util.* 和 import java.sql.*这两个包中都有Date类,对Date的引用不明确,此时会出现歧义,导致编译出错;这种情况下需要使用完整的类名,即方法一。
方法三
例:
//100平方根
public class Date {
public static void main(String[] args) {
System.out.println(Math.sqrt(100));
}
}
使用import static 导入包中的静态方法和字段
import static java.lang.Math.*;
public class Date {
public static void main(String[] args) {
System.out.println(sqrt(100)); // 100的平方根
System.out.println(max(10,100)); // 10和100之间最大值
System.out.println(min(10,100)); // 10和100之间最小值
}
}
运行结果
10.0
100
10
包在定义时,需要尽量指定成唯一的名字
常见的包:
- java.lang:系统常用基础类(String、Object),从JDK1.1后自动导入。
- java.lang.reflect:java 反射编程包;
- java.net:进行网络编程开发包。
- java.sql:进行数据库开发的支持包。
- java.util:是java提供的工具程序包。(集合类等) 非常重要
- java.io:I/O编程开发包。
七、static成员
例如,在学生类中,学生的姓名,年龄等信息是具体描述一个学生的,而上课的教室并不是描述学生的,所以教室是静态成员变量。
static修饰成员变量
static修饰的成员变量,成为静态成员变量
- 不属于某个具体的对象,是所有对象所共享的
- 可以通过对象访问,也可以通过类名访问(推荐)
- JDK7及其以前,HotSpot(Java虚拟机)中存储在方法区(Java虚拟机不止有HotSpot),JDK8后,类变量存储在Java堆中
随类的加载而创建,随类的卸载而销毁(JVM将类加载到其内部之后,类实际也是一个对象)
public class Student {
public String name; // 姓名
public String gender; // 性别
public String id; // 学号
public short age; // 年龄
public static String classRoom = "f306"; // 就地初始化
public static void main(String[]args) {
Student s1 = new Student(); // 通过new实例化对象
//通过类名访问静态成员变量
System.out.println(Student.classRoom);
//通过对象访问静态成员变量
System.out.println(s1.classRoom);
}
}
static修饰成员方法
static修饰成员方法
若 Student 类中 classRoom 为 private 类,则在另外一个类StudentTest 中无法通过类名访问,则可以在 Student 类中添加一个public 静态方法
public static String getClassRoom(){
return classRoom;
}
1、被static修饰的成员方法成为静态成员方法,是类方法,不是某个对象所特有的
2、 可以通过对象调用,也可以通过类名.静态方法(…)调用(推荐)
3、静态方法没有隐藏的this引用参数,所以不能在静态方法中访问任何非静态成员变量
4、静态方法中不能调用任何非静态方法(原因:非静态方法中有this参数,在静态方法中调用时无法传递this引用)
//编译失败
public static String getClassRoom(){
System.out.println(this); // 静态方法不能访问任何非静态
return classRoom;
}
//编译失败
public static String getClassRoom(){
age += 1; //静态方法中不能访问实例变量
return classRoom;
}
//编译失败
public static String getClassRoom(){
doClass(); // 静态方法中不能访问实例方法
return classRoom;
}
八、代码块
代码块:使用{ }定义的一段代码
分为普通代码块、构造块、静态块、同步代码块(多线程部分)
普通代码块
public class Main {
public static void main(String[] args) {
// 直接使用{}在方法中定义
{
int a = 10;
System.out.println(a);
}
}
}
构造代码块
也叫实例代码块,定义在类中的代码块(不加修饰符),一般用于初始化实例成员变量。
public class Student {
public String name; // 姓名
public String gender; // 性别
public String id; // 学号
private static String classRoom = "f306";
// 实例代码块
{
this.name = "蓝胖子";
this.gender = "女";
this.id = "28256";
System.out.println("姓名: " + name + " 性别: " + gender + " id: " + id);
}
public static void main(String[] args) {
Student s1 = new Student();
//通过类名访问静态成员变量
System.out.println(Student.classRoom);
//通过对象访问静态成员变量
System.out.println(s1.classRoom);
}
}
运行结果
姓名: 蓝胖子 性别: 女 id: 28256
f306
f306
创建对象的时候,实例代码块会执行,每创建一个对象,实例代码块就执行一次。且实例代码块优先于构造方法执行(原因:编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法[用户所写]的第一条语句前)
静态代码块
被 static 定义的代码块称为静态代码块,一般用于初始化静态成员变量,在类加载阶段执行
public class Student {
private String name; // 姓名
private String gender; // 性别
private String id; // 学号
private static String classRoom ;
//静态代码块
static {
classRoom = "f305";
System.out.println("静态代码块");
}
public static void main(String[] args) {
Student s1 = new Student(); // 通过new实例化对象
Student s2 = new Student();
}
}
运行结果
静态代码块
1、静态代码块不管构造多少个对象,只会执行一次
2、如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并,最终放在生成的<>方法中,该方法在类加载时调用,并且只调用一次。
public class Demo {
{
System.out.println("实例代码块1");
}
{
System.out.println("实例代码块2");
}
static{
System.out.println("静态代码块1");
}
static{
System.out.println("静态代码块2");
}
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
}
}
运行结果
静态代码块1
静态代码块2
实例代码块1
实例代码块2
实例代码块1
实例代码块2
构造了两个对象,但只执行了一次
九、内部类
内部类:将一个类定义在另一个类的内部(也是封装的一个体现)
外部类:将一个类定义在一个方法内部
public class Demo {
class InnerClass{
}
}
Demo 是外部类, InnerClass 为内部类。
注意:Java的一个源文件中可以存放多个类,但不能成为内部类,最多只能有一个被 public 修饰,如下
public class Demo {
}
class Test{
}
内部类和外部类都有自己的字节码文件。
内部类的分类
public class Demo {
//成员内部类,分为普通内部类和静态内部类
//普通内部类
class InnerClass1{
}
//静态内部类
static class InnerClass2{
}
//在方法中定义内部类,统一称为局部内部类
//实例代码块中
{
class InnerClass3{
}
}
//静态代码块中
static{
class InnerClass4{
}
}
//方法中定义内部类
public void method(){
class InnerClass5{
}
//普通代码块中定义内部类
{
class InnerClass6{
}
}
}
}
普通内部类
在同一个类中访问
public class TestClass {
int a;
public void methodA(){
}
//普通内部类
public class InnerClass{
int b;
void methodB(){
}
}
public static void main(String[] args) {
//普通类:成员使用,先构造对象,通过对象访问其成员
TestClass ti = new TestClass();
ti.methodA();
//使用成员内部类中的普通内部类
//借助外部类的对象创建内部类的对象
InnerClass ic = ti.new InnerClass();
ic.methodB();
}
}
在同一个包,不同类中访问
void func(){
TestClass ti = new TestClass();
//实例化TestClass中内部类的对象
TestClass.InnerClass tii = ti.new InnerClass();
}
在一个内部类中可以直接使用外部类的方法
public class TestClass {
int a;
int b;
public void methodA(){
}
//普通内部类
public class InnerClass{
int b; // 编译器会给内部类增加一个引用类型的变量 TestClass this$0;用来指向外部类的对象
void methodB(){
a = 10;
methodA();//可以引用外部变量和方法
b = 20;//外部和内部都定义了b,给内部类自己的b赋值,就近原则
TestClass.this.b = 30;//给外部的b赋值
}
}
}
外部类中,不能直接访问内部类中的成员,如果要访问必须先要创建内部类的对象。
静态内部类
访问同一个类中静态内部类
public class TestStaticClass {
int a;
int b;
static int c;
void methodA(){
System.out.println("methodA()");
}
static class InnerClass{
int d;
static int e;
void methodC(){
}
}
public static void main(String[] args) {
//静态成员变量访问
System.out.println(TestStaticClass.c);
//静态内部类对象的创建
//不需要借助外部类对象进行创建
InnerClass ic = new InnerClass();
}
}
访问同一个包,不同类中的静态内部类
//静态内部类
public void MethodA(){
//静态成员变量
System.out.println(TestStaticClass.c);
}
//静态内部类对象
//可以直接创建,不需要依赖外部类对象
public static void main(String[] args) {
TestStaticClass.InnerClass tic = new TestStaticClass.InnerClass();
}
在静态内部类中不能直接访问外部类中的实例变量
局部内部类
- 局部内部类只能在所定义的方法体内部使用
- 不能被public、static等修饰符修饰
- 编译器也有自己独立的字节码文件,命名格式:外部类名字$x内部类名字.class,x是一个整数。
- 几乎不会使用
十、对象的打印
public class Person {
private String name;
private String gender;
private int age;
//Alt+Insert--constructor--Ctrl多选会自动生成
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
//私有成员需要提供set\get方法
//Alt+Insert--set/get--Ctrl多选会自动生成
public void setAge(){
this.age = age;
}
public int getAge(){
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Person p = new Person("蓝胖子","女",18);
System.out.println(p);
}
}
运行结果
note20220327.Person@1540e19d
打印的地址,并未打印对象的值
如果要打印对象的值,需要重写 toString 方法。
//Alt+Insert--toString会自动生成
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
Person p = new Person("蓝胖子","女",18);
System.out.println(p);
}
运行结果
Person{name='蓝胖子', gender='女', age=18}