目录
一、引言
Java是一门面向对象的语言,那么什么是对象呢?在面向对象的世界里,一切皆为对象。emmm,看起来还是不太好理解,没关系,我们可以带着这个疑问在接下来的内容中继续感知。
对象的问题算是解决了一半,那什么又是面向对象呢?官方一点的说法是,面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。举个例子来说,当你要洗衣服的时候,你、衣服、洗衣液、洗衣机就是四个对象,要完成洗衣服这个任务,你需要做的,就是将衣服放在洗衣机中,倒上洗衣液,启动洗衣机,洗衣机就会完成整个洗衣过程从而完成这个任务,你作为整个流程的操作者,只需要将衣服放到洗衣机中就可以了,不需要关注洗衣机是怎么注水、旋转、烘干的,这就是面向对象。
通过面向对象的方式来处理的好处就是,不需要关注更加具体的实现过程,只要完成对象之间的交互就可以到达目的。
需要注意的是,面向过程和面向对象并不是一门语言,而是解决问题的方法,二者没有好坏之分,都有其专门的应用场景
二、类的定义
2.1简单认识类
类是用来对一个(实体)对象进行描述的,主要描述该实体(对象)具有哪些属性,哪些功能,描述完成之后计算机就可以识别了
2.2类的定义格式
在Java中定义类时需要用到关键字class,具体定义方法如下
class ClassName{
field;//字段(属性)或者成员变量
method;//行为或者成员方法
}
其中,class为定义类的关键字,ClassName为类的名字,{}中为类的主体
类中包含的内容称为类的成员,属性主要是用来描述类的,称之为类的成员变量或者类成员变量;方法主要说明类具有哪些功能,称为类的成员方法
tips:
- 类名注意采用大驼峰定义
- 一般一个文件当中只定义一个类
- main方法所在的类一般要使用public修饰
- public修饰的类必须要和文件名相同
- 不要轻易修改public修饰的类的名称,如果要修改,选中该文件,右键 Refactor->Rename,(不能直接改变类的名称)
三、类的实例化
3.1什么是实例化
当我们定义了一个类,就相当于在计算机中定义了一种新的数据类型(类似于C语言中的结构体),有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)
用类类型创建对象的过程,称为类的实例化,使用关键字new配合类名来实例化对象
例如:
public class Main{
public static void main(String[] args){
Example example = new Example();//其中Example是一个类
}
}
tips:
- new关键字用于创建一个对象的实例
- 使用“ . ”来访问对象中的属性和方法
- 同一个类可以创建多个实例
3.2类和对象的说明
- 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员
- 类是一种自定义的类型,可以用来定义变量
- 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
- 形象点来说就是,类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样,类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
四、this引用
4.1为什么要有this引用
当形参名不小心于成员变量名相同时,二者之间的赋值就存在了疑惑,到底是谁给谁赋值,成员变量给成员变量?参数给参数?参数给成员变量?成员变量给参数?要解开这个疑惑,就需要用到this引用
4.2什么是this引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过this引用去访问。只不过所有的操作对用户都是透明的,即用户不需要来传递,编译器自动完成
例如:
public class Date{
public int year;
public int month;
public int day;
public void setDay(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(this.year+"/"+this.month+"/"+this.day);
}
}
4.3this引用的特性
- this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
- this只能在“成员方法”中使用
- 在“成员方法”中,this只能引用当前对象,不能再引用其他对象
- this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
五、对象的构造及初始化
5.1如何初始化对象
在Java方法内部定义一个局部变量时,必须初始化,负责会编译失败,例如下面的代码:
public static void main(String[] args){
Date d = new Date();
d.printDate();
d.setDate(2024.5.18);
d.printDate();
}
但是这样的初始化存在着一个问题:每次对象创建好之后都需要调用SetDate方法并且设置初值才能完成初始化,每次都这样赋值会很麻烦。如何解决这个问题呢,我们引入了构造方法
5.2构造方法
5.2.1概念
构造方法(也成为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的声明周期内只调用一次。例如:
public Date(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
tips:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间
5.2.2特性
- 构造方法的名字必须与类名相同
- 构造方法没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
- 如果用户没有显示定义,编译器会生成一份默认的构造方法,生成的构造方法一定是午餐的
- 但是用户一旦定义,编译器则不再生成
- 构造方法中,可以通过this调用其他构造方法来简化代码
- 据大多数情况下使用public来修饰,在某些特殊场景下会被private修饰
tips:
- this(……)必须是构造方法中第一条语句
- 不能形成环
5.3默认初始化
不知道你有没有注意到。局部变量在使用时必须要初始化,但是成员变量好像并不用特意初始化就可以使用,这是为什么呢?
想搞清楚这个过程,就要知道new关键字背后发生的一些事情,例如如下的代码
Date d = new Date(2024,5,18);
在程序层面的简单一行代码,在JVM层面可是经历了好一番复杂过程,简单概括为以下几点:
- 检测对象是否加载了,如果没有加载则进行加载(加载:将字节码文件载入虚拟机解析成对象)
- 为对象分配内存空间
- 处理并发安全问题(例如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突)
- 初始化所分配的空间(即:对象空间被申请好了之后,对象中包含的成员已经设置好了初始值)不同数据类型对应的初始值如下
数据类型 默认值 byte
0 char
'\u0000' short 0 int 0 long 0L
boolean false float 0.0f double 0.0 reference null - 设置对象头信息
- 调用构造方法,给对象中各个成员 赋值
5.4就地初始化
在声明成员变量时,就直接给出了初始值(代码编译完成后,编译器会将所有成员初始化的这些语句添加到各个构造函数中)
六、封装
6.1封装的概念
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。简单来说就是套壳屏蔽细节
还拿之前的洗衣机举例,在洗衣机工作的过程中,我们是看不到其内部是如何实现滚筒的转动、衣物的烘干等工作的,提供给我们的只有操作面板上的若干按键。作为一个使用者,我们也并不关心其内部构造是什么,只需要知道如何使用就好了。这就是很典型的封装。
6.2访问限定符
Java中主要通过类和访问权限来实现封装。类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
范围 | private | default | protected | public |
同一包中的同一类 | √ | √ | √ | √ |
同一包中的不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的非子类 | √ |
tips:
- protected主要是用在继承中
- default权限指:什么都不写时的默认权限
- 访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
- 一般情况下成员变量设置为private,成员你方法设置为 public
6.3封装扩展之包
6.3.1包的概念
为了更好的管理类,把多个类收集在一起成为一组,成为包。包是对类、接口等封装机制的体现,是一种对类或者接口等的很好的组织方式。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
6.3.2导入包中的类
使用关键字import导入包,例如:
import java.util.Arrays;//导入Arrays类
import java.util.*;//导入util中的所有类
6.3.3自定义包
基本原则:
- 在文件的最上方加上一个package语句指定该代码在哪个包中
- 包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式
- 包名要和代码路径相匹配
- 如果一个类没有package语句,则该类被放到一个默认包中
6.3.4常见的包
- java.lang:系统常用基础类,此包从JDK1.1后自动导入
- java.lang.reflect:Java反射编程包
- java.net:进行网络编程开发包
- java.sql:进行数据库开发的支持包
- java.util:Java提供的工具包
- java.io:I/O编程开发包
七、static成员
7.1静态成员
如果一个类的若干个对象中有相同的成员变量或者成员方法,就可以用static来修饰这个成员变量,此时这个被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,而是所有对象所共享的
7.2static修饰成员变量
static修饰的成员变量,称为静态成员变量
静态成员变量特性:
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类卸载而销毁)
7.3static修饰成员方法
被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的
静态方法特性:
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名。静态方法名方式调用,更推荐使用后者
- 不能在静态方法中访问任何非静态成员变量
- 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用的时候无法传递this引用
- 静态方法无法重写,不能用来实现多态
7.4static成员变量初始化
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化和静态代码块初始化,就地初始化就是在定义时直接给出初始值,至于静态代码块初始化,我们先来了解一下代码块
八、代码块
8.1代码块概念以及分类
使用{}定义的一段代码称为代码块,分局代码块定义的位置以及关键字,又可以分为以下几种:
- 普通代码块
- 构造块
- 静态块
- 同步代码块
8.2普通代码块
定义在方法中的代码块,一般不使用
public class Main{
public static void mian(String[] args){
{
int x = 10;
System.out.println("x="+x);
}
}
}
8.3构造代码块
构造块:定义在类中的代码块(不加修饰符) ,也叫实例代码块,一般用于初始化实例成员变量
public class Student{
private String name;
private int age;
public Student(){
System.out.println("Already init.....");
}
//实例代码块
{
this.name="nobody";
this.age=18;
}
}
8.4静态代码块
使用static定义的代码块称为静态代码块,一般用于初始化静态成员变量
public class Student{
private String name;
private int age;
static{
name="nobody";
age=18;
}
}
tips:
- 静态代码块不管生成多少个对象,都只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行
- 实例代码块只有在创建对象时才会执行
九、内部类
9.1内部类的概念
将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现
tips:
- 定义在class类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
- 内部类和外部类共同用一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
9.2内部类的分类
根据内部类定义的位置不同,一般可以分为以下几种形式:
- 成员内部类:普通内部类(未被static修饰的成员内部类)、静态内部类(被static修饰的成员内部类)
- 局部内部类、匿名内部类
9.2.1实例内部类
未被static修饰的成员内部类
tips:
- 外部类中的任何成员都可以在实力内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实力内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员来访问
- 实例内部类对象必须在先有外部类对象前提下才能创建
- 实例外部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实力内部类中的成员,如果要访问必须先要创建内部类的对象
9.2.2静态内部类
被static修饰的内部成员称为静态内部类
tips:
- 在静态内部类中只能访问外部类中的静态成员
- 创建静态内部类成员时,不需要先创建外部类对象
9.2.3局部内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少
tips:
- 局部内部类只能在所定义的方法体内部使用
- 不能被public、static等修饰符修饰
- 编译器也有自己独立的字节码文件,命名格式:外部类名字¥数字内部类名字.class