目录
[1. 基础概念](#1. 基础概念)
[1.1 面向过程和面向对象](#1.1 面向过程和面向对象)
[1.2 类和对象](#1.2 类和对象)
[2. 类的创建](#2. 类的创建)
[2.1 类的基础创建格式](#2.1 类的基础创建格式)
[2.2 自动创建类 和 类的命名规则](#2.2 自动创建类 和 类的命名规则)
[3. 类实例化成对象 (对象的创建)](#3. 类实例化成对象 (对象的创建))
[3.1 类类型](#3.1 类类型)
[3.2 什么是实例化?怎样实例化?](#3.2 什么是实例化?怎样实例化?)
[3.3 实例化的意义 ------ 类和对象的关系](#3.3 实例化的意义 —— 类和对象的关系)
[4. 对象的初始化](#4. 对象的初始化)
[4.1 默认初始化](#4.1 默认初始化)
[4.2 就地初始化](#4.2 就地初始化)
[5. 类和对象中涉及到的生命周期和作用域(初步了解)](#5. 类和对象中涉及到的生命周期和作用域(初步了解))
[5.1 类、对象、实例变量](#5.1 类、对象、实例变量)
[5.2 成员变量 和 成员方法](#5.2 成员变量 和 成员方法)
[5.3 局部变量 和 全局变量【与c不同】](#5.3 局部变量 和 全局变量【与c不同】)
[6. 构造方法](#6. 构造方法)
[6.1 构造方法的概念和特性](#6.1 构造方法的概念和特性)
[6.2 构造方法的创建和使用](#6.2 构造方法的创建和使用)
[6.2.1 默认的构造方法](#6.2.1 默认的构造方法)
[6.2.2 无参数的构造方法](#6.2.2 无参数的构造方法)
[6.2.3 带多个参数的构造方法](#6.2.3 带多个参数的构造方法)
[6.2.4 构造方法的重载](#6.2.4 构造方法的重载)
[6.3 自动创建构造方法和成员方法](#6.3 自动创建构造方法和成员方法)
[6.4 构造方法的调用时期](#6.4 构造方法的调用时期)
[7. this关键字的功能](#7. this关键字的功能)
[7.1 this的概念(用this指向当前对象)](#7.1 this的概念(用this指向当前对象))
[7.2 用this区分成员变量和局部变量](#7.2 用this区分成员变量和局部变量)
[7.3 用this调用构造方法](#7.3 用this调用构造方法)
[7.4 this的类型 与 方法的链式调用](#7.4 this的类型 与 方法的链式调用)
1. 基础概念
1.1 面向过程和面向对象
面向对象和面向过程,这两者都是生活上的概念;面向对象编程和面向过程编程是由生活概念衍生或拓展出来的 编程概念。
一、面向过程
-
生活概念:
**面向过程强调的是按照步骤或流程来解决问题,这与日常生活中的"按部就班"非常相似。**例如,烹饪一道菜时,需要先准备食材,然后按照一定的顺序进行烹饪,最后装盘上桌。
-
编程概念:
面向过程是一种以事件为中心的编程思想,编程时把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。
二、面向对象
-
生活概念:
面向对象强调的是将问题分解为多个相互关联的对象,并通过对象之间的交互来解决问题。这与日常生活中的"团队合作"或"分工合作"相似。 例如,一个公司可以看作是由多个部门组成的对象集合,每个部门都有自己的职责和功能;我们不需要具体知道每个部门内部是如何运作,我们只需要知道每个部门能够做什么 ,通过部门之间的协作来实现公司的整体目标。
-
编程概念:
**面向对象编程是一种以对象为基本单位的编程方式。**它将数据和操作数据的函数(方法)封装在一起,通过对象间的交互来完成任务。面向对象编程适用于复杂、需要高度模块化和可扩展性的系统,如大型软件、游戏开发等。它注重整体性和对象之间的交互,通过类和对象的抽象来解决问题。
1.2 类和对象
类是多个对象所具有的相似属性和特点的抽象集合。
"类和对象"一般都是指编程上的概念(而不是生活上的概念)。 前面说了,面向对象编程是一种以对象为基础单位的编程方式,而对象是依靠类的语法而得以存在的。
我们以一个例子来说明:(面向对象------洗衣服)
- 在现代,洗衣服这个事情涉及到4个对象:人、衣服、洗衣粉、洗衣机。
(因为是面向对象,所以为了把衣服洗好,我们不需要关心衣服有多少对应多少洗衣粉、人要分几次才把衣服放完、洗衣机要滚动多少圈放多少次水............++我们只需要知道这4个对象的交互可以把衣服洗好++)
- 这4个对象并没有相似的属性和特点,所以我们需要用到4个类,以洗衣机类为例。
提取所有洗衣机实体的所具有的属性和特点:
我们可以使用下面这个"类"来描述洗衣机对象了:
洗衣机类:
属性:产品品牌,型号,产品重量,外观尺寸,颜色...
功能:定时....
该例子中的洗衣机对象:
- 属性:
- 产品品牌: 樱花
- 型号: XPB150-1505
- 产品重量: 39kg
- 外观尺寸: 850 x 500 x 1000(单位mm)
- 颜色: 白色
- ......
- 功能:
- 标准净洗
- 烘干
- 快洗
- ......
- 类和对象是一种编程上的概念,而编程是讲究语法的,在Java中我们可以简单写成这样:
cpp
class WashMachine{
String brand = "樱花"; // 品牌
String type = "XPB150-1505"; // 型号
double weight = 30; // 重量
double length = 850; // 长
double width = 500; // 宽
double height = 1000; // 高
String color = "白色"; // 颜色
void washClothes(){ // 普通洗衣服
System.out.println("洗衣45分钟");
}
void dryClothes(){ // 脱水
System.out.println("脱水功能");
}
void quickWash(){ //快洗
System.out.println("快洗21分钟");
}
}
(这只是比较简单的写法,实际上还会用到很多访问限定符和其他的关键字)
2. 类的创建
2.1 类的基础创建格式
在java中定义类时需要用到 class关键字:
class 类名{
成员变量;
成员方法;
}
注意:我们习惯是把所有的成员变量写在开头; 当然,如果成员变量的创建写在调用该成员变量的成员方法后面,语法上也是支持的。
类中包含2种成员:
- 成员变量:用于存储对象的状态。它们可以是基本数据类型(如int、float等)或引用类型(如String、数组等)。
- 成员方法:用于描述对象的行为或功能。
2.2 自动创建类 和 类的命名规则
除了自己来写类,我们也可以通过系统来帮我们创造一个无成员的类,具体步骤如下:
步骤1~3:
完成后会弹出下面这个窗口,步骤4~6:
最后会生成一个访问限定符为public的类:
【注意事项】和 类的命名规则
①. 我们自动创建类,其实是创建了一个.java文件。该类由public修饰,且该类的名字和文件的名字相同。
(观察上面的两张图,确实是这样)
②. 硬性要求:
- 一个.java文件内只允许存在一个由public修饰的类,且此类的名字必须和文件名相同;
- 如果该文件有其他类并列存在,则其他类不能被访问限定符修饰。
【所以最好的书写建议:一个文件一个类】
例如:
③. 访问限定符有public、protected、private(具体的功能在下一篇文章讲),它们不仅可以修饰类,还能修饰成员变量和成员方法。目前成员变量和方法我们都先用着public,main方法先用着public static。
④.软性建议:类的命名采用大驼峰命名法 ,即每个单词的首字母都采用大写字母。
比如刚刚的洗衣机类:WashMachine。
3. 类实例化成对象 (对象的创建)
3.1 类类型
定义了一个类,就相当于在计算机中定义了一种新的类型,该类的类型就称为类类型。
例如:
cpp
public class Dog {
public String name;
public String color;
public void barks(){
System.out.println(name+":汪汪汪~");
}
public void wag(){
System.out.println(name+":摇尾巴~");
}
}
这里的Dog就是一种类类型。
类类型也是一种引用类型,用类类型创建的变量称为实例变量,实例变量存储的是地址。
3.2 什么是实例化?怎样实例化?
用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
以刚刚的Dog类为例:
cpp
public class Main{
public static void main(String[] args) {
Dog dogh = new Dog(); //通过new实例化对象
dogh.name = "阿黄";
dogh.color = "黑黄色";
Dog dogs = new Dog();
dogs.name = "大白";
dogs.color = "白色";
}
}
- "对象的创建" ,也叫"对象的实例化 ",也可以叫做"类实例化成对象";这几种说法都一样。
- 实例化具体是:new一个类,给出一块空间来存储一个对象,而该对象所需要的空间大小根据所属类的大小来判断(这就是为什么前面说:在面向对象编程中,对象是依靠类而存在的)。比如这里的" Dog dogs = new Dog() "。
- Dog类被new出来后,我们就说创建了一个Dog类的对象。
- 变量dogs被称为实例变量,引用着这个Dog类实例化后的对象。使用" . "来访问对象中的属性和方法。
类名后面的括号不能省略,这与后面要讲到的构造方法有关。
而后面的内容,比如" dogs.name = "大白"; dogs.color = "白色"; ",其实是属于 对象的初始化。
3.3 实例化的意义 ------ 类和对象的关系
做个比方:类相当于一个模板或蓝图 ,用于创建具体的对象。对象则是类的实例化结果,它是具体的实体,拥有类所定义的属性和方法。
4. 对象的初始化
我们知道,Java中对变量的初始化检查是很严格的:在main方法中变量没有初始化就使用,会编译报错。为什么在类中声明的成员变量不初始化也不会报错呢?
4.1 默认初始化
当我们new了一个对象的时候,如果没有赋值初始化,对象里面的属性(成员变量)会被赋予默认值。
例如:
cpp
public class Dog {
public String name;
public int age;
}
class Main{
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.name);
System.out.println(dog.age);
}
}
输出结果:
默认值和数组的默认初始化是一样的:整数是0,小数是0.0,布尔型是false,引用类型都是null......
4.2 就地初始化
在声明成员变量时,就直接给出了初始值。
cpp
public class Dog {
public String name = "大白";
public int age = 4;
}
class Main{
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.name);
System.out.println(dog.age);
}
}
其实在实例化3.2中的例子,像这种在main方法中直接对成员变量赋值的情况是很少的。因为一般一个类,所有的成员变量都是用private修饰,在其他类的main方法中就不能直接使用private修饰的成员变量了。
5. 类和对象中涉及到的生命周期和作用域(初步了解)
5.1 类、对象、实例变量
-
类 :
- 生命周期:
- 从类加载开始,到程序运行结束后卸载。其中类加载发生在编译阶段。
- 作用域:
- 类的作用域受访问限定符限制,它们都属于包级的访问权限。
- 生命周期:
-
对象:
- 生命周期:
- 从对象被实例化开始(从new出来开始),到 程序结束 或 没再被实例变量引用 时被回收。
- 作用域:
- 由类实例化,所以也受访问限定符限制。
- 生命周期:
-
实例变量:
- 生命周期:
- 实例变量属于特殊的局部变量,可以引用实体对象。在main方法中被创建,到程序结束时回收。
- 作用域:
- 作用于方法体中。
- 生命周期:
5.2 成员变量 和 成员方法
成员变量和成员方法++都是对象的属性和功能++ ,所以是从对象被实例化开始存在,到对象被回收时结束。(生命周期与对象一致)
方法与函数的不同:
- 在C语言中,函数的定义必须写在所有函数的外面。而在Java中,方法的定义,必须写在类里面。
- 在C语言中,只要有头文件声明该函数的存在,在别的.c文件中就可以使用该函数。而在Java中,方法的使用还受到访问限定符的修饰,不是所有的包和类都可以使用该类下的方法。
5.3 局部变量 和 全局变量【与c不同】
局部变量
Java局部变量的定义:
在Java中,如果变量的创建或声明处于类的里面、方法的外面,则该变量是成员变量;如果变量的创建和声明处于方法的里面,则该变量是局部变量。
例如:
在Java中,局部变量不能被访问限定符修饰。
例如:
可以看到,用访问限定符public修饰局部变量,编译器会报错。
Java局部变量的生命周期与作用域,++与C语言是类似的。++从变量声明开始,到方法执行完毕或代码块结束时结束。定义域仅限于声明它的方法或代码块内部。
全局变量
在Java中已经不存在全局变量的概念了。因为在C语言中,全局变量的创建位于所有函数和代码块的外面;而在Java中,变量的声明至少也必须在类的里面。
不过也可以通过静态变量来时实现全局变量的功能,比如public类前提下的public static变量。静态变量的内容在下一篇博客会讲,这里简单提一下。
6. 构造方法
6.1 构造方法的概念和特性
构造方法的定义:
构造方法是一种没有返回值的成员方法。
构造方法的特性:
- 构造方法也叫构造器,是一种特殊的成员方法。
- 没有返回值类型,设置为void也不行。
- 名字必须与类名相同。
- 在整个对象的生命周期内只调用一次。
例如:
cpp
public class Person {
public String name;
public int age;
Person(){ //构造方法
name = "人";
age = 25;
}
}
class Test1{
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
}
}
输出结果:
可以发现,此处的构造方法Person()方法是无返回值类型的。
如果构造方法的名字与类名不相同,则会报错。如果某方法与类名一致,但是有返回值,则该方法只是一个普通的成员方法,只不过名字恰好和类名相同。
构造方法也可以被访问限定符修饰,一般都是由public修饰。
6.2 构造方法的创建和使用
6.2.1 默认的构造方法
如果用户没有显式定义构造方法,编译器会生成一份默认的构造方法。
- 生成的默认构造方法一定是无参的。
- 默认构造方法的方法体一般是无内容的。(等学到继承会有点不同)
- 默认构造方法的访问修饰符与所在类的访问修饰符一致。
例1:(usage是IDEA给的提示,与代码无关)
例2:
6.2.2 无参数的构造方法
自己显式定义的没有参数的构造方法。在创建对象时,写法和无显式定义构造方法时一样,例如"Class c = new Class(); "。
注意:一旦自己显示定义了构造方法,编译器就不会生成默认构造方法。(救急不救穷)
例如:
cpp
class Person {
public String name;
public int age;
Person(){ //显示定义构造方法
name = "person";
age = 0;
}
}
class Test2{
public static void main(String[] args) {
Person person = new Person(); //与无构造方法的写法相同
System.out.println(person.name);
System.out.println(person.age);
}
}
输出结果:
它并不等于:
这里报错是因为两个构造方法没有构造方法重载,因为两个方法的参数列表完全相同。
6.2.3 带多个参数的构造方法
显示定义了带参数的构造方法且要使用该方法时,在new阶段就要写够参数。例如"Class c = new = Class(参数1,参数2,......参数n);"。
当然,还是救急不救穷。显示定义了构造方法,编译器就不会生成默认构造方法。
例如:
cpp
class Person {
public String name;
public int age;
Person(String s, int n){
name = s;
age = n;
}
}
class Test3{
public static void main(String[] args) {
Person person = new Person("小明",18); //补足参数列表
System.out.println(person.name);
System.out.println(person.age);
}
}
6.2.4 构造方法的重载
构造方法也是可以构成方法重载的。
因为构造方法名必须和类名相同,那么我们只需要保证参数列表不同。
至于在创建对象时使用的是哪一个构造方法?系统会根据你输入的参数个数、参数类型和参数顺序来判断所使用的构造方法。
例如:
cpp
class Person {
public String name;
public int age;
Person(){
}
Person(int n){
age = n;
}
Person(String s, int n){
name = s;
age = n;
}
}
class Test3{
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person(34);
Person p3 = new Person("小军",20);
System.out.println(p1.name + ": " + p1.age);
System.out.println(p2.name + ": " + p2.age);
System.out.println(p3.name + ": " + p3.age);
}
}
输出:
6.3 自动创建构造方法和成员方法
IDEA集成开发工具为我们提供了自动创建构造方法和成员方法的功能。
构造方法(Constructor)
详细步骤:
成员方法(Getter和Setter)
详细步骤:
创建成功后就会有get和set方法了:
6.4 构造方法的调用时期
对于一个简单的构造方法调用和对象的创建,例如:Person p = new Person("小明",18).
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
4. 初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值
5. 设置对象头信息(关于对象内存模型后面会介绍)
6. 调用构造方法,给对象中各个成员赋值
7. this关键字的功能
7.1 this的概念(用this指向当前对象)
先看一个日期类的例子:
cpp
public class Date {
public int year;
public int month;
public int day;
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
}
class Main{
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
// 对d1,d2,d3的日期设置
d1.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();
d2.printDate();
d3.printDate();
}
}
输出结果:
我们想要成员变量的year、month、day被形参初始化赋值,可得到的结果都是0,这是为什么?
因为当成员变量和局部变量重名时,使用局部变量的使用优先级会高于成员变量。
【这与C语言中,全局变量与局部变量重名时类似,局部变量的优先级也会高于全局变量】
这时候,Java提供一个this关键字解决了这个问题。还是刚刚的例子,我们用this关键字:
cpp
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
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
}
class Main{
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
// 对d1,d2,d3的日期设置
d1.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();
d2.printDate();
d3.printDate();
}
}
输出结果:
为什么可以这样?
因为this可以引用当前对象。
比如有一个" Person p = new Peron("小明") ",对象中的构造方法有" this.name = name ",那么这条语句就相当于:p.name = name。
注意:this引用的是当前对象,而不是当前类。(这一点会在下一篇的static部分讲解)
7.2 用this区分成员变量和局部变量
这在刚刚的例子中就有所体现,这里补充一点:
**this是"成员方法"第一个隐藏的参数,编译器会自动传递。**在成员方法执行时,编译器会负责将调用成员方法 对象的引用传递给该成员方法,this负责来接收。
cpp
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;
}
}
这个类中的成员方法就相当于下面这个类中的成员方法:
cpp
public class Date {
public int year;
public int month;
public int day;
public void setDay(Date this, int year, int month, int day){ //隐藏的参数this
this.year = year;
this.month = month;
this.day = day;
}
}
7.3 用this调用构造方法
this调用构造方法的要求:
- 当存在多个构造方法的时候,可以用"this(参数表)"的方式在一个构造方法中调用另一个构造方法。
- this(......) 语句只能在构造方法中。(因为构造方法只能被调用一次)
- this(......) 语句必须处于构造方法的第一行 。(这是硬性要求,Java语言的设计者为了保证成员变量在正确初始化之前不被使用)
- 构造方法不能递归调用 。(因为构造方法只能被调用一次)
对于要求1的错误例子:只有一个构造方法的时候也调用this(...)
cpp
class Date {
public int year;
public int month;
public int day;
public Date(){
this(); //只有一个构造方法也使用this()
}
}
这个方法就相当于public Date(){ Date();}。形成了递归结构,而构造方法是不能递归调用的。
要求2的错误例子:this(...)写在了成员方法里面
要求3的错误例子:this(...)没写在第一行
要求4的错误例子:多个构造方法成环
7.4 this的类型 与 方法的链式调用
this的类型
this引用的是当前对象,对象的类型就是this的数据类型。(属于类类型)
比如刚刚的Date类,里面所有的this它的数据类型都是Date类型。
方法的链式调用
在Java中,方法的链式调用是一种常见的编程技术,它允许在一个表达式中连续调用多个方法。
链式调用的特点:
每次对象调用的成员方法,其返回值均是该对象。
例如:
cpp
class Person {
public String name;
public int age;
public Person setName(String name) { //返回类型是Person类型
this.name = name;
return this; //返回当前对象
}
public Person setAge(int age) { //返回类型是Person类型
this.age = age;
return this; //返回当前对象
}
}
class Main{
public static void main(String[] args) {
Person p = new Person().setName("小明").setAge(18); //链式调用
System.out.println(p.name+":"+p.age);
}
}
输出:
这里的调用逻辑是:先创建对象并调用构造方法,返回该对象后再调用setName方法,而setName的返回值也是该对象,再用返回值对象调用setAge方法,最终setAge方法返回对象给实例变量p。
注意:链式访问不是链式调用。链式访问是:一个方法的返回值,被作为另一个方法的参数。
假如有一个加法方法add:
System.out.println(add(add(1, 2), 3));
最里面add方法的返回值,也是外面add方法的参数;而外面add方法的返回值,同时也是println方法的参数。
本期分享完毕,感谢大家的支持Thanks♪(・ω・)ノ