0 对象与类
💡类(class)是构造对象的模板或蓝图,由类构造(construct)对象的过程称为创建类的实例(instance)。
📌封装(encapsulation,有时称为数据隐藏)是与对象有关的一个重要概念。从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。对象中的数据称为实例域(instance field),操纵数据的过程称为方法(method)。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态(state)。
0.0 自定义类
📌
Java
中所有的类都继承自Object
一个最基本类的定义如下:
class <ClassName> {
// 实例域(属性)
field1
field2
...
// 构造器(类对象的初始化方法)
constructor1
constructor2
...
// 方法
method1
method2
...
}
💡一般情况下,习惯于将每一个类存在一个单独的源文件中。如 Person
类单独放在 Person.java
文件中
0.1 类方法的访问权限
四种访问权限
public
:公开级别,对外公开。protected
:受保护级别,对子类和同一个包中的类公开。默认
:向同一个包的类公开。private
:只有类本身可以访问,不对外公开。
访问修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
默认 | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
0.2 封装
权限的使用
虽然可以用
public
标记实例域,但这是一种极为不提倡的做法。public
数据域允许程序中的任何方法对其进行读取和修改。这就完全破坏了封装。因此在封装过程中,首先进行属性进行私有化private
(不能直接修改属性);然后提供public
的setxxx()
方法,用于对属性的判断并赋值;最后提供public
的getxxx()
方法,用于获取属性的值。
0.3 构造器
⚡ 构造器基本特点
① 构造器与类同名; ② 每个类可以有一个以上的构造器; ③ 构造器可以有0个、1个或多个参数; ④ 构造器没有返回值; ⑤ 构造器总是伴随着new操作一起调用
0.3 显式参数与隐式参数
😶🌫️显式参数:方法里传入的形参 😱隐式参数:类方法中调用的公共属性(可以用
this
表示隐式参数)
class Person {
...
String name;
public void say(String info) {
System.out.print("Hello " + this.name + ". " + info)
}
}
0.4 final实例域
- 🐵
final
最终的意思,可以修饰类、属性、方法、局部变量(悟空定身术,不让改)final
修饰类:类不能被继承final
修饰属性:属性的值不能被更改,成为常量
,一般用AA_BB_CC
的形式来命名。其在定义的时候必须赋值,有三个地方可以赋值:1>
定义时就赋值;2>
在构造器中赋值(如果该属性还是静态的就不行);3>
在代码块中final
修饰方法:该方法不能被重写final
修饰局部变量:该变量变为局部常量,不能被更改
- 😎 如果一个类是
final
类,就没有必要再将用final
修饰该类的方法 - 😬
final
不能修饰构造器 - 😱
final
和static
搭配使用效率更高,不会导致类加载- 解释:访问静态属性时会导致类加载,然后执行静态代码块;但是将该静态属性再用
final
关键字修饰,再访问该静态属性时,静态代码块就不会被执行了
- 解释:访问静态属性时会导致类加载,然后执行静态代码块;但是将该静态属性再用
- 👻包装类(Integer、Double、Float、Boolean都是
final
类),String也是final
类
1 静态域与静态方法
1.0 静态域
静态的关键字为 static
,用该关键字标识的域(属性)就是 静态域
class Person {
public static int nextID = 10;
public int age;
}
📌如以上代码中,创建 1000 个
Person
类的实例,就有 1000 个 实例域 age,但是只有一个 静态域nextID
。所有的类实例共用一个nextID
域。即使没有一个 Person 对象,静态域 nextID 依然存在。
1.1 静态常量
静态变量一般用得比较少,更常用的是静态常量,即将 static
与 final
结合使用。
public static final double PI = 3.1415926;
📌静态常量与常量的区别,当没有 static
关键字时,PI
常量只能对过对应的 类对象 进行访问(先创建实例对象,再访问里面的值),多个实例对象就有多个常量。使用 static
即可通过 类名 直接访问该 静态常量,且有多个实例对象时,也共用一个 静态常量。
1.2 静态方法
静态方法是一种不能向对象实施操作的方法,是没有 this
参数的方法。(因为静态方法可以在不创建类实例的情况下直接通过类名调用,所以不能使用和具体类实例绑定在一起的对象)
❌以下第一个例子是❌错误❌错误❌错误的❌
class Person {
public static int nextID = 10;
public int age;
// 该静态方法是错误的示例
public static int getAge() {
return this.age;
}
// 以下是正确的静态方法
public static int getNextID() {
return nextID;
}
}
2 方法参数
方法参数分为基本数据类型与对象引用。
- 基本数据类型:采用 按值调用。即方法得到的是所有参数值的一个拷贝,方法不能修改传递给它的任何参数变量的内容
- 对象引用:是对引用进行了拷贝(所以),和地址传递是有区别的
- 总结
Java
中 方法参数 的使用情况:- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象
3 对象构造
构造器定义了对象的初始状态,而 Java
提供了多种编写构造器的机制。
3.0 重载
🌟如果多个方法有相同的方法名、不同的参数,便产生了重载。
StringBuilder a = new StringBuilder();
StringBuilder b = new StringBuilder("hello");
3.1 默认域初始化
默认域初始化
😶🌫️🚩如果构造器没有显示的给域赋予初值,则域会自动赋为默认值:数值为0、布尔值为false、对象引用为null。 📌强烈建议做好初始化操作,避免程序不符合预期⚡
3.2 无参构造器
无参构造器
⚡很多类都包含一个无参数的构造函数,对象由无参数构造函数创建时,其状态会设置为适当的默认值。 📌如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数构造器。
3.3 显式域初始化
显式域初始化
⚡通过重载类的构造器方法,可以采用多种形式设置类的实例域的初始状态。确保不管怎样调用构造器,每个实例域都可以被设置为一个有意义的初值,这是一种很好的设计习惯。 🌟在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,直接赋值的方式很有用。如下:
class Person {
// 该步骤的赋值在构造器之前执行
private String name = "";
}
3.4 初始化块
数据域的初始化方法有3种,前两种分别为在构造器中赋值与在声明中赋值,第三种就是在初始化块中赋值。如下:
class Person {
private String name;
private static int age;
// 初始化块
{
name = "";
}
// 静态的初始化块
static
{
age = 18;
}
}
📌创建一个类实例时,首先运行声明中赋值,其次为初始化块,最后才是构造器。
3.5 finalize方法
finalize方法
任何一个类都可以添加
finalize
方法,该方法在垃圾回收器清除对象之前调用。📌📌📌在实际应用中,不要依赖于使用finalize方法回收任何短缺的资源,这是因为很难知道这个方法什么时候才能够调用。 🌟如果某个资源需要在使用完毕后立刻被关闭,那么就需要由人工来管理。对象用完时,可以应用一个close
方法来完成相应的清理操作。
4 包
包
标准的
Java
类库分布在多个包中,包括java.lang
、java.util
和java.net
等。标准的Java
包具有一个层次结构。使用包的主要原因是确保类名的唯一性。(lang
包默认引入,不用手动引入)
⚡要将一个类放入包中,就必须将包的名字放在源文件开头,包中定义类的代码之前。
package cn.hkc.demo;
import java.math.BigInteger;
public class hello {
public static void main(String[] args) {
...
}
}
📌如果没有在源文件中放置
package
语句,这个源文件中的类就被放置在一个默认包(defaulf package)中。默认包是一个没有名字的包。
5 类路径
- 类存储在文件系统的子目录中,类的路径必须与包名匹配。
- 类文件也可以存储在
JAR
文件中。在一个JAR
文件中,可以包含多个压缩形式的类文件和子目录,这样既可以节省又可以改善性能。 JAR
文件使用ZIP
格式组织文件和子目录。可以使用所有ZIP
实用程序查看内部的rt.jar
以及其他的JAR
文件
一个类为了使多个项目都可以引用,就可以将
jar
包统一放在一个目录里,之后配置类路径即可java -classpath ~/classdir:.:~/jarpath MyProgram
6 类的设计技巧
6.0 基本设计方法
- [p] 数据私有:绝对不要破坏封装性。有时候,需要编写一个访问器方法或更改器方法,但是最好还是保持实例域的私有性。
- [p] 数据初始化:
Java
不对局部变量进行初始化,但是会对对象的实例域进行初始化。最好不要依赖于系统的默认值,而是应该显式地初始化所有的数据。 - [p] 不要在类中使用过多的基本类型:用其他的类代替多个相关的基本类型的使用。这样会使类更加易于理解且易于修改。
- [p] 将职责过多的类进行分解:如果明显地可以将一个复杂的类分解成两个更为简单的类,就应该将其分解。
- [p] 优先使用不可变的类:更改对象的问题在于,如果多个线程试图同时更新一个对象,就会发生并发更改。其结果是不可预料的。如果类是不可变的,就可以安全地在多个线程间共享其对象。
6.1 单例设计模式
📌 单例设计模式:就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,该类只提供一个取得对象实例的方法。
🌟有两种方式:饿汉式和懒汉式。 其实现方式:
1>
构造器私有化(不能new对象了)2>
类的内部创建对象3>
向外暴露一个静态的公共方法
😶🌫️ 饿汉式
class Cat {
private String name;
public static int age = 18;
private static Cat cat = new Cat("Tom"); // 内部创建对象
private Cat(String name) { // 构造器私有化
this.name = name;
}
public static Cat getCat() { // 静态的公共方法
return cat;
}
}
以上这个
cat
类,构造器的权限为private
,则程序不能够new
对象,只能内部获取,则需要调用getCat()
方法,而静态方法只能访问静态属性,所以内部创建的cat
对象用到了static
关键字。通过以上写法,Cat
类在整个程序中只可能有一个实例
🤪 懒汉式
class Cat {
private String name;
public static int age = 18;
private static Cat cat;
private Cat(String name) { // 构造器私有化
this.name = name;
}
public static Cat getCat() { // 静态的公共方法
if (cat == null) {
cat = new Cat("Tom"); // 内部创建对象
}
return cat;
}
}
🚨 两种方式最大的区别在于:
对象创建的时机不同
。当执行Cat.age
调用这个类的静态属性时,这个类会被加载,所以饿汉式中对象会被创建,如果我们最后并没有使用这个对象,就造成了资源浪费;而懒汉式中则不会创建,只有需要用到这个对象实例,才会创建
- [I] 🪶 两种方式对比说明
- [>] 饿汉式不存在线程安全问题,懒汉式存在线程安全问题(多个线程同时要创建对象)
- [>] 饿汉式可能存在资源浪费,懒汉式使用时才会创建