1. 类与对象的初步认知
我们都知道Java是一门面向对象的语言,c语言是一门面向过程的语言,那么什么是面向对象,什么又是面向过程呢?我们在Java中一直提到的类和对象又是什么呢?
面向对象:面向对象就是把现实中的事务都抽象成为程序设计中的“对象”,其基本思想是一切皆对象,先设计组件,再完成拼装。
面向过程:注重的是过程,在整个过程中所涉及的行为,就是功能。
类和对象:类就是一类对象的统称。对象就是这一类具体化的一个实例。举个例子,人就是一个类,而具体到某个人,如"张三",这就是一个对象,一个类可以产生无数个对象。
2. 类和类的实例化
在Java中,用class来创建一个类,创建一个类就创建了一个新的数据类型,创建一个类的格式如下:
class Person{
//这里存放类的变量和方法
}
class创建了一个类,这个类名字是Person,属于引用类型,{ }里就是类的主体。类中的变量称为:成员变量。类中的方法称为:成员方法。
那么,什么叫做类的实例化呢?
有了类,我们只是相当于有了一个抽象的概念,在人和张三那个例子里,相当于只是有了人这个概念,但是还没有具体的实体(也就是对象),想要创建一个实体,就要进行类的实例化。 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间。
类的实体化的过程如下:
类名 对象名 = new 类名; //例: Person person = new Person();
上面提到过类属于引用类型,引用类型和基本数据类型的区别在于,引用类型需要内存的分配和使用,在类型的实例化中,关键字new的作用就是在堆上申请一块内存空间,也就是说,只要使用引用类型,就需要关键字new来分配内存空间。
3. 类的成员
类的成员包括以下内容:成员变量,成员方法,代码块,内部类和接口等,这里我们重点介绍前三个。
3.1 字段/属性/成员变量
成员变量:在类的内部,在方法的外部定义的变量是成员变量,成员变量又叫字段,属性。
成员变量包括普通成员变量和静态成员变量,普通成员变量又叫实例变量,静态成员变量又叫类变量。
实例变量用public修饰,类变量的定义要在public后加上static:
//定义实例变量: public 变量类型 变量名; //例: public int age; //定义类变量: public static 变量类型 变量名; //例: public static int high;
定义好了实例变量,该如何使用呢?
我们一般通过对象名.变量名来使用这个对象的实例变量,如:
class Person{ //创建类
public int age; //在类中定义实例变量
}
Person person1 = new Person(); //实例化对象
System.out.println(person1.age); //通过对象名.变量名使用对象并输出
来看下面这段代码:
class Person{
public int weight=0;
}
public class test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.weight++;
System.out.println(person1.weight);
person2.weight++;
System.out.println(person1.weight);
}
}
//结果:
1
1
在class类中我们定义了一个实例变量weight,在主函数中实例化了两个对象person1和person2,这两个对象都有自己的实例变量,对每个对象的变量进行运算,不影响其他对象中的变量。
而对于类变量,情况则不同:
class Person{
public static int age=0;
}
public class test {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.age++;
System.out.println(person1.age);
person2.age++;
System.out.println(person2.age);
}
}
//结果:
1
2
分别对两个对象的类变量进行运算,得到的结果似乎与实例变量不同,好像两个对象使用的是同一个类变量?
实际上,实例变量是每个对象都有一个,每个对象的实例变量之间互不影响,毫无关系,但类变量只有一个,它被放在JVM的方法区中,所有对象共用一个类变量,这就是所谓的实例变量依赖于对象,而类变量不依赖于对象。
既然不依赖于对象,那么通过对象名.变量名这种方法来使用类变量就有些不合适了(注:通过这种方法使用类变量并不会编译错误,只是不合理),正确的使用类变量的方法应该是 类名.变量名
3.2 方法 (method)
类中的成员方法包括实例方法和类方法,使用方式和区别与实例变量和类变量类似,这里不再赘述,方法中还有一种特殊的方法称为 构造方法(construction method)
构造方法是在实例化对象的时候会被自动调用到的方法, 方法名字和类名相同, 用于对象的初始化。
虽然我们前面已经能将属性就地初始化, 但是有些时候可能需要进行一些更复杂的初始化逻辑, 那么就可以使用构造方法.
后面我们会详细介绍构造方法的语法。
4. 封装
什么叫封装?
软件开发的本质就是对程序复杂程度的管理. 如果一个软件代码复杂程度太高, 那么就无法继续维护. 如何管理复杂程度? 封装就是最基本的方法.
在我们写代码的时候经常会涉及两种角色: 类的实现者和类的调用者.
封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的, 只要知道如何使用类就行了.这样就降低了类使用者的学习和使用成本, 从而降低了复杂程度.
4.1 private实现封装
private/ public 这两个关键字表示 “访问权限控制” 。
被 public 修饰的成员变量或者成员方法, 可以直接被类的调用者使用.,被 private 修饰的成员变量或者成员方法, 不能被类的调用者使用。
换句话说, 类的使用者根本不需要知道, 也不需要关注一个类都有哪些 private 的成员. 从而让类调用者以更低的成本来使用类。
直接使用public:
class Person{
public String name;
public int age;
}
public class test {
public static void main(String[] args) {
Person person = new Person();
person.name = "zhangsan";
person.age = 18;
System.out.println("name: "+person.name+", age: "+person.age);
}
}
//结果:
name: zhangsan, age: 18
这样的代码导致类的使用者(main方法的代码)必须要了解 Person 类内部的实现, 才能够使用这个类. 学习成本较高,一旦类的实现者修改了代码,那么类的使用者就需要大规模的修改自己的代码, 维护成本较高。
使用private对类进行封装:
class Person {
private String name = "zhangsan";
private int age = 18;
public void print() {
System.out.println("name: "+name+", age: " + age);
}
}
class test {
public static void main(String[] args) {
Person person = new Person();
person.print();
}
}
使用private对类进行进行封装,可以避免main函数直接接触Person类的内部,当需要对类内部的代码进行修改时,只需要在类的内部进行修改,类的使用者不需要进行任何处理。
通常情况下我们会把字段设为 private 属性, 但是方法是否需要设为 public, 就需要视具体情形而定. 一般我们希望一个类只提供 “必要的” public 方法, 而不应该是把所有的方法都设为public
4.2 getter和setter方法
当我们使用private对类的内部进行封装时,无法再从类的外部使用这个字段了,当我们需要从类的外部获取或者修改这个字段时,就需要用到getter和setter方法。 getter方法,表示获取这个成员的值,setter 方法表示设置这个成员的值。
在Java中,getter和setter方法不需要自己书写,我们可以让编译器自动为private修饰的变量提供getter和setter方法,操作步骤如下:
在编译器中用alt + Insert快捷键打开生成栏,选择Getter和Setter ,选择想要生成Getter和Setter方法的成员变量,即可为其自动生成Getter和Setter方法。
5.构造方法
构造方法是一种特殊的方法,每当我们使用new关键字来创建关键字时便会自动调用该方法。
5.1 基本语法
构造方法的名称必须与类的名称相同,并且没有返回值,如果类中没有构造函数,则编译器会自动生成一个不含参数的构造方法。若类中定义了构造方法,则默认的无参构造将不再生成。
每个类中可以不止有一个构造方法,因为构造方法的名称必须与类名一致,即所有构造方法的名称都相同,想要创建不同的构造方法就必须保证传入构造方法的参数不同,那么这些构造方法正好构成重载。
构造方法代码实例:
5.2 this关键字
6. 认识代码块
字段的初始化方式有:
- 就地初始化
- 使用构造方法初始化
- 使用代码块初始化
前两种方式已经介绍完成,现在介绍第三种方法.
6.1 什么是代码块
代码块:使用 {} 定义的一段代码.
代码块可以分为四种:
- 普通代码块
- 构造代码块
- 静态代码块
- 同步代码块(暂时不需要理解)
6.2 普通代码块
public class Main{
public static void main(String[] args)
{
{ //直接使用{}定义,普通代码块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}
// 执行结果
x1 = 10
x2 = 100
普通代码块用法较为少见
6.3 实例代码块
实例代码块是定义在类中的代码块,又叫构造代码块,实例代码块一般用于初始化实例成员变量。
class Person{
private String name;//实例成员变量
private int age;
private String sex;
public Person() {
System.out.println("I am Person init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
p1.show();
}
}
// 结果
I am instance init()!
I am Person init()!
name: bit age: 12 sex: man
6.4 静态代码块
使用static定义的代码块。一般用于初始化静态成员属性。
class Person{
private String name;//实例成员变量
private int age;
private String sex;
private static int count = 0;//静态成员变量 由类共享数据 方法区
public Person(){
System.out.println("I am Person init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
//静态代码块
static {
count = 10;//只能访问静态数据成员
System.out.println("I am static init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();//静态代码块是否还会被执行?
}
}
静态代码块不管生成多少个对象,其只会执行一次,且是最先执行的。
静态代码块执行完毕后, 实例代码块(构造块)执行,再然后是构造函数执行。



