Java 封装与类
一、面向对象编程
面向对象编程三大特性:封装、继承和多态。
类是实现封装的手段,是面向对象编程的基本单元。
封装隐藏了类的内部实现细节,暴露给外界可控的操作,提高数据的完整性和安全性,提高模块的可重用性和开发效率,降低开发的复杂性。
从面向对象程序设计方法的角度看,程序中所有的东西都是对象,而程序是对象的组合,对象间通过消息传递实现协作。
二、类
类是Java程序设计的核心概念和基本单元。
类是对一类事物的特征的描述,是抽象后的描述;对象是一个实例。
1、Java没有全局变量,只有:
静态变量:隶属于类
实例变量:类体中定义,类的属性,隶属于对象。
局部变量:形参、方法内定义或代码块中定义,隶属于方法。
2、成员修饰符——public、private、protected、default
作用:实现访问权限控制。
public:被public修饰的成员,可以在任何一个类中被调用。
private:除了本类的方法能够调用私有成员(方法、属性),其他任何类都不能直接访问私有成员。
所有修饰符都能修饰数据成员、方法成员和构造方法;只有public和default能修饰类(外部类)。
3、方法重载
(1)方法名相同
(2)方法的参数签名不相同(参数类型、个数、顺序)。
(3)方法的返回类型可以不同,方法的修饰符也可以不同
三、对象
3.1 构造方法
与类名同名,不返回任何数据类型(实际上没有返回任何东西),没有void,不能有任何非访问性质的修饰符。
类中可以有多个构造方法——应用了方法重载,每个构造方法在被调用后将在堆内存中创建一个对象然后向调用者返回一个该对象的引用(这里的引用,是指对象在堆内存空间中的首地址)
this,表示一个对象引用,其值指向正在执行方法的对象;
特别地,构造方法中,通过this调用其他构造方法时,必须放在第一行;
构造方法只能通过this调用一次其他构造方法;
关键字new的作用是(调用)一个构造函数,为对象分配内存空间,返回这块内存的引用。
如果没有显示定义构造方法,类将会隐式定义一个默认情况下不接受任何参数的构造函数并将所有实例变量初始化为默认值。
原始数字类型的实例变量默认值为0,布尔类型变量为false,引用类型变量为null。
只要没有将类中的变量进行初始化,它就会有默认值。但是final变量可以把初始化放到构造方法中进行,而static final必须在声明时就进行初始化。
3.2 创建对象
创建对象的过程即类实例化的过程,包括类的加载、对象内存分配和变量初始化。
(1)new显示创建
Cat ketty=new Cat();
new操作符,在内存中为对象开辟空间,它在堆内存上为对象开辟空间,动态地分配内存(主要存放对象的实例变量)。“动态分配“的意思是在程序运行时内存才会被分配,这样你可以创建任意数量的对象。
ketty称为对象引用,表示可以指向一个Cat类型的实例。ketty位于栈中。
等号赋值,将右边在堆中创建的对象的内存空间的首地址赋予了对象的引用变量ketty。
(2)调用java.lang.Class类的newInstance()方法
Java中的任何类都包含有有一个Class类,它描述该类的元数据。通过调用Class类的类方法,创建对象:
Cat ketty=Cat.getClass().newInstance();
或者使用动态类加载语句(需存在相应的.class文件即可)创建:
Cat ketty=(Cat) Class.forName("Cat").newInstance();
通过类加载器方式创建类必须保证被创建的类在搜索路径中存在相应的.class文件。
newInstance()方法创建对象实例会调用无参构造函数,所以必须确保类中存在无参数的构造函数,否则会有异常。
private修饰构造方法:
有时希望控制对象的生成或者阻止直接通过new在类的外部生成类的实例,这时用private修饰构造方法。单例设计模式就是通过此方法实现只创建一个对象的实例。
public class Singleton{
private static Singleton instance=null;
private Singleton(){
System.out.println("private singleton is caled!")
}
public static Singleton getInstance(){
if(instance==null)
instance=new Singleton();
return instance;
}
}
3.3 使用对象
适用对象必须通过对象的引用,像c中的指针,但不能像指针那样直接修改引用值,而是只能通过它执行对象操作。
将一个对象的引用赋值给另一个引用时,实际上复制的是对象的引用地址,结果是两个引用指向同一个对象。
3.4 Java的方法的参数传递机制——值传递
当传递一个参数时,方法获得该参数的一个副本。
事实上,传递的参数会有俩种:基本类型的变量和对象的引用变量。
基本类型的变量,意味着变量值本身被复制,并传递给方法,因此,方法中对变量的修改不会影响原变量;
对象的引用变量,即对象的引用值(堆中的首地址)被复制,传递给方法。方法中的操作可以影响对象。
3.5 垃圾回收机制
一个对象不再被使用,应该回收该对象占用的内存空间以及资源,从而提高内存利用率。
Java中,内存的回收由垃圾回收器(Garbage Collection)自动处理。处理流程:
- 查看堆内存,区分出正在使用的对象和未使用的对象;
- 删除已经废弃的对象;
3.6 如何区分一个对象是否任然会被使用呢?
判断标准——程序持有一个指向对象的引用;若一个对象,不被程序的任何部分持有引用,则判定它的内存是可以被回收的。
GC是通过JVM一个后台的工作线程来完成,它周期性地扫描堆内存来发现程序不再使用的对象。为了判断对象是否可以被释放,需要引用计数和对象引用遍历。引用技术记录了对特定对象的所有引用数,这意味着,当应用程序创建引用或者引用已经超出声明周期时,JVM必须适当地增减引用计数。当对象的引用数为0时,便可以进行垃圾收集。
3.7 手动启动GC
垃圾回收器是JVM自动启动的,它不受程序代码的控制,其具体执行的时间也不确定,但是开发者可以利用System.gc()手动强制启动垃圾回收气销毁无用对象。
GC机制还提供了finalize()方法,用于释放不是通过new操作符创建的对象空间。这种情况一般发生在使用“本地方法(Native Interface)”的情况下,本地方法是一种在Java中调用非Java代码的方式。
finalize()方法会在GC释放对象时被执行,即要是我们手动调用了System.gc(),finalize()也会一起执行。
四、实例变量和类变量
4.1 static静态变量(类变量)
所有对象共享类变量。
程序执行时,类的字节码被加载到内存,如果该类没有创建对象,类的实例成员变量不会被分配内存。类中声明的类变量在该类被加载到内存时,就已经被分配了相应的内存空间,直到程序退出运行才释放所占用的内存。而如果该类没有被创建对象,类的实例成员变量不会被分配内存。
从数据操作的角度来看,实例方法既能对类变量进行操作,又能对实例变量进行操作;而类方法只能对类变量进行操作。
实例变量子在申明时如果没有被赋予初值,将被编译器初始化为NULL(引用类型),0,或者false,字符变量默认是空。
类变量在类的初始化之前初始化,无论类的实例被创建多少个,类变量只在初始化时被分配一次内存空间。
不同的对象将被分配到不同的堆内存空间
4.2 静态代码块和非静态代码块
static {…}为静态代码块,{…}直接括起来的为非静态。
凡是static修饰的,都将最先在类被加载时,按排列的位置执行(当然你不能在静态变量还没有初始化前就使用它,即你的静态代码块如果在静态变量的声明之前,就无法使用,但是可以直接初始化它);
非静态代码块在类初始化创建实例时,将会被提取到类的构造器中执行,而且,非静态代码块会比构造器中的代码块先执行。
public class StaticDemo{ static{ name="static-block-1"; System.out.println("static block 1 is over!"); } public static String name="Fancy"; private String mail="myEmail"; static{ System.out.println(name); name="static-block-2"; System.out.println("static block 2 is over!"); } public StaticDemo(){ mail="110@qq.com"; System.out.println("Constructor has been excuted and the name is "+name); } static{ System.out.println(name); name="static-block-3"; System.out.println("static block 3 is over!"); } { mail="119@qq.com"; System.out.println("Non-static block has been excuted"); } public void setName(String name){ this.name=name; System.out.println("SetName is called."); } public static void main(String[] args) { StaticDemo staticDemo=new StaticDemo(); staticDemo.setName("Jay"); } } //output: static block 1 is over! Fancy static block 2 is over! static-block-2 static block 3 is over! Non-static block has been excuted Constructor has been excuted and the name is static-block-3 SetName is called.
4.3 不可变对象
一旦创建就不能被更改。(没有给外界提供可以修改的操作而已,并没有多特别)
它的类为不可变类,“不可变”是一种很好的性质,它使得对象的行为是可预测的。它可以被其它对象随意引用,而不用担心不可变对象在某个时刻被修改。
优点:
- Java中的所有不可变对象都是线程安全的。
- 构造、测试和使用都很简单;
- 当用作其它类的属性时不需要被保护性复制;
- 可以很好地用作Map集合和Set集合的元素;
编写一个不可变类,就不要提供任何可以修改对象状态的方法。