JavaSE知识分享——类和对象(下)
若文中出现疏漏、不足之处,望广大读者批评指正
接下来我会持续更新JavaSE、Java数据结构、MySQL、JavaEE、微服务、Redis等等内容的知识点整理。后续我也会精心制作算法解析、项目经验系列内容,内容绝对干货。相信这些文章能够成为我和大家的“葵花宝典”,喜欢的话就关注一下吧!敬请期待!
文章目录
- 一、封装
- 认识封装
- 访问限定符
- 认识封装中的包
- 导入包中的类
- 自定义包
 
 
- 二、static关键字
- static修饰成员变量
- static修饰成员方法
- static修饰成员变量的初始化
 
- 三、代码块
- 代码块概念以及分类
- 普通代码块及构造代码块
- 静态代码块
 
- 四、对象的打印
- 默认行为:
- 重写 toString()
 
一、封装
认识封装
- 面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。
何为封装呢?简单来说就是套壳屏蔽细节。
- 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
如:计算机厂商在出厂时,在外部套上壳 子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
访问限定符
- Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。
- Java中提供了四种访问限定符:
  
 注:在 Java 中,局部变量不能使用访问修饰符(如 public、private、protected)或 static 等修饰符。唯一允许的修饰符: final
- 局部变量可以声明为 final ,表示该变量只能被赋值一次(常量)
认识封装中的包
- 为了更好的管理类,把多个类收集在一起成为一组,称为软件包。包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。
- 包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
- 包对应着文件夹,可在每个包中建立多个.Java文件。文件夹是包在文件系统中的物理存储形式,二者是“逻辑组织”与“物理载体”的关系,确实不是直接等同。
导入包中的类
使用 import语句导入包
import java.util.Date;
- 然后就可以直接使用 Date 这个类
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
- 不使用import导入包的话就只能这样写,很麻烦:
java.util.Date date = new java.util.Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
- 如果使用 import java.util.*的话,就会导入java.util 包中的所有类。但是还是更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况.
可以使用import static导入包中静态的方法和字段:
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// 静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}
注意事项: import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要.import 只是为了写代码的时候更方便,不用再像上述那样麻烦地导入包中的类了。
自定义包
基本规则
- 包名要和代码路径相匹配. 例如创建com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
 如果一个类没有 package 语句, 则该类被放到一个默认包中.
注意:
-  com.example 是一个完整的包名。但从目录层次看,它依赖于父目录 com 的存在。在逻辑上, com 和 com.example 是两个不同的包。 
-  com.example包中需要时是否需要导入父包中的类? 需要。必须使用 import 语句,就像导入任何一个其他无关的包(例如 import java.util.List; )一样。 
-  额外限制:当然即使导入了,目标类(如 com.Tool )还必须被声明为 public ,否则在包外依然没有访问权限。 
二、static关键字
static修饰成员变量
-  之前在Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量)。 
-  在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,也不用依靠对象的引用来访问,是所有对象所共享的。 
-  static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共用的。 
静态成员变量特性:
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 通过类名来访问是规范的,也可以通过对象访问,但一般更推荐使用类名访问。
- 类变量存储在方法区当中,这是与普通成员变量最显著的区别。普通成员变量需要依靠对象的引用来访问,都是由类来创建一个引用变量存储对象的地址,以此来访问对象,此- 引用变量在栈区分配的内存。由对象存储真实数据(成员变量、及成员方法实现),- 对象在堆区分配内存。而如果是类变量,则- 直接与普通变量分离,在内存中的方法区分配空间储存数据,所以也就完全不依赖对象,即使还- 没有通过 new 实例化对象,照样可以使用,通过类名访问方法区的类变量即可,而这块空间也就是所有此类所实例化的对象公用的,压根就不是和对象一样在堆区。
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
static修饰成员方法
Java中,被static修饰的成员方法称为静态成员方法,也是是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
 静态方法特性:
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者。
- 不能在静态方法中访问任何非静态成员变量,静态方法只能访问静态变量。
- 静态方法中不能直接调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用。静态方法不能直接调用非静态方法是因为缺少隐式的 this 引用。但通过创建对象实例来调用是完全合法的,因为对象提供了必要的上下文。
 - 关键点说明:
-  this 引用的作用:实例方法隐含一个 this 参数,指向调用该方法的对象。静态方法没有 this ,因此不能直接调用实例方法。 
-  通过对象实例调用:当实例化对象后(new一个对象),该对象就有了完整的实例上下文,通过对象调用实例方法时, this 自然指向该对象。 
static修饰成员变量的初始化
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性(非静态成员变量)。
- 静态成员变量的初始化分为两种:就地初始化(定义时就直接赋值)和静态代码块初始化。
三、代码块
代码块概念以及分类
使用 { } 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下几种:
- 普通代码块
- 构造块
- 静态块
还有同步代码块(这个涉及到后续知识)
普通代码块及构造代码块
-  普通代码块:定义在方法中的代码 
-  构造代码块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。 构造代码块一般用于初始化实例成员变量
注:构造代码块的核心作用是抽取所有构造方法的公共初始化逻辑,避免重复代码;而构造方法则负责实现各自独特的初始化逻辑,二者分工不同。
简单来说,构造代码块是“通用模板”,构造方法是“个性化定制”。比如一个 Person
类,所有对象初始化时都需要“初始化血型”(公共逻辑,写在构造代码块),但不同对象的“姓名、年龄”不同(独特逻辑,写在构造方法)。
静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
//实例代码块
{
this.name = "xxx";
this.age = 12;
this.gender = "man";
System.out.println("I am instance init()!");
}
// 静态代码块
static {
classRoom = "xxx306";
System.out.println("I am static init()!");
}
public Student(){
System.out.println("I am Student init()!");
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
}
}
注意事项:
- 静态代码块无论生成多少个对象,其只会在类加载时被触发一次。
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并),而实例代码块只有在创建对象时才会执行。
四、对象的打印
在 Java 中,当你想直接打印一个对象时,会自动调用该对象的 toString() 方法。
默认行为:
- 所有 Java 对象都继承自 Object 类,Object 类有一个 toString() 方法
- toSrting默认实现返回: 类名@哈希码 (十六进制),例如: com.example.Person@1b6d3586
打印时的自动调用:
Person person = new Person("张三", 25);
System.out.println(person); // 实际上相当于 System.out.println(person.toString());
//输出结果就是一串哈希码(哈希码不是地址)
重写 toString()
默认的 toString() 返回的信息对调试不太友好,所以通常我们会在当前类中重写它:
public class Person {private String name;private int age;// 构造方法等其他代码...@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}
注:重写时方法签名必须完全匹配!不能搞成重载了。
//正确重写
public String toString() {//实现}
这样打印时就会显示更有意义的信息: Person{name='张三', age=25} ,toString()方法返回什么就打印什么。
觉得文章对你有帮助的话就点个赞,收藏起来这份免费的资料吧!也欢迎大家在评论区讨论技术、经验
