类和对象
当我们没有去了解过java的知识点中 不免产生一些问题: 什么是类?什么是对象?
记住一句话:在java当中 一切皆对象
类:是用来描述一个对象的
而对象是一个真正存在的实体
在Java这门纯面向对象的语言中 我们只需要以面向对象方式来进行处理问题 而不需要关注处理问题的过程
我们以洗衣服为例:
总共有4个对象:人、衣服、洗衣粉、洗衣机
整个洗衣服的过程:人将衣服放进洗衣机、倒入洗衣粉、启动洗衣机、洗衣机就会完成洗衣过程。
而我们面向对象的语言 不需要关注整个过程 只需要知道是由人、衣服、洗衣粉、洗衣机这四个对象之间交互完成即可
注意:面向过程和面相对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景
总结:
1.在java当中 一切皆对象
2.在java当中 关注的是对象(对象与对象之间 进行协作完成)
类定义和使用
面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如:洗衣机。但是计算机并不认识洗衣机,需要开发人员告诉计算机什么是洗衣机(洗衣机的各个属性)
简单认识类
类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了
比如:洗衣机,它是一个品牌,在Java中可以将其看成是一个类。
属性:产品品牌,型号,产品重量,外观尺寸,颜色...
功能:洗衣,烘干、定时....
在Java语言中,如何对上述的洗衣机类来进行定义呢?
类的定义格式
在java中定义类时需要用到class关键字,具体语法如下
class 类名{
成员变量/字段/属性:定义在类当中,方法的外边
成员方法/行为
}
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类具有哪些功能,称为类的成员方法
那我们定义一个洗衣机的类来看看:
注意事项:
- 类名采用大驼峰定义
- 成员前写法统一为public 后面会详细解释
- 此处写的方法不带static关键字 后面会详细解释
我们了解了什么是类 如何定义一个类 那类如何去用呢?又如何初始化呢?
这里就需要对象的实例化啦
类的实例化
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是java语言自带的内置类型,而类是用户自定义了一个新的类型。有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)
我们先定义一个Dog类
这里我在定义类的成员方法时 使用到了this引用 我们之后会有讲解
用类 类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
如:
Dog dog=new Dog();
注意事项:
1.new关键字用于创建一个对象的实例
2.使用 . 来访问对象中的属性和方法
3.同一个类可以创建多个实例
类和对象的说明
1. 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
2. 类是一种自定义的类型,可以用来定义变量.
3. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类的成员变量
4. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
this引用
为什么要有this引用
我们先看一个例子:
以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打印 看了代码不免产生疑问?
1.这么多对象 都调用了同一个setData方法 在这个方法内 是怎么区分是哪个对象调用了year month day呢?
这时候也就需要this出场了 this具体是什么我们接下来讲解
2.如果形参名y、m、d变成year、month、day呢?(形参名和成员变量同名)又会发生什么呢?
形参自己给自己赋值 并没有修改到对象当中的year、month、day
参数名与类的成员变量名相同 导致参数隐藏了成员变量 在setData方法内部 实际上是将参数的值赋给了参数本身 而不是类的成员变量
而又因为成员变量在声明时会有一个默认值 如果它们在声明时没有显式的初始化 这些默认值取决于变量的类型 如:int类型 默认值为0
问题2的解决方案:加this关键字
在 Java 中,方法参数和类的成员变量是放在不同的作用域中的。当在方法内部有一个参数和一个成员变量同名时,Java 会默认先引用参数。为了访问类的成员变量,需要使用 this 关键字来明确指定
什么是this引用?
this引用指向 当前对象(成员方法运行时 调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成
this 引用 是指向 调用成员方法的 对象实例的,它允许你访问该对象的成员变量和调用该对象的其他成员方法。
比如说:
这里this引用指向d2(调用成员方法setData的对象实例)
this引用的特性
1. this的类型:对应类的类型引用,即哪个对象调用就是哪个对象的引用类型
2. this只能在"成员方法"中使用
3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
注意:
this不能在静态方法当中使用(静态方法:定义方法时 会有static)
this的用法有三种:
1.可通过this 访问 当前对象 的 成员变量
2.可通过this 访问 当前对象 的 非静态的成员方法
3.可通过this 访问 当前对象 的 其他构造方法(紧接着进行讲解)---this()
对象的构造及初始化
如何初始化对象
通过前面知识点的学习知道,在Java方法内部定义一个局部变量时,必须要初始化,否则会编译失败。
要让上述代码通过编译,非常简单,只需在正式使用a之前,给a设置一个初始值即可。
如果是对象:
需要调用之前写的setDate方法才可以将具体的日期设置到对象中。通过上述例子发现个问题:
每次对象创建好后 再调用setDate方法设置具体日期,比较麻烦,那对象又该如何初始化?
使用构造方法(马上讲解)
注意点:
1.局部变量不进行初始化 会编译失败
2.成员变量不进行初始化 会有默认值(取决于变量的类型)
构造方法
概念:构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次
作用:初始化 对象 当中的成员
普通方法定义:
返回值 方法名(形参列表){
方法体
}
构造方法定义:没有返回值
方法名(形参列表){
方法体
}
运行输出:
通过这个例子 我们可以知道 在实例化对象的时候 可以借用 构造方法对 对象进行初始化
结论:实例化对象的时候 一定会调用构造方法
构造方法特性
- 方法名 和 当前类名一样
- 没有返回值类型
- 一般情况下使用public修饰
- 在创建对象时 由编译器自动调用 并且在对象的生命周期内只调用一次
- 构造方法可以重载
- 绝大多数情况下使用public来修饰,特殊场景下会被private修饰
一个对象的生成 至少有2步很重要:
1.为对象分配空间
2.调用合适的构造方法
既然说 需要调用合适的构造方法 那我们之前好像并没有写过啊?
结论:
其实 当我们没有写 任何构造方法的时候
Java会帮我们提供一个默认的不带参数的构造方法
但是 注意 一旦 我们写了构造方法 java就不会再给我们提供不带参数的构造方法了
补充
关于this()
通过this() 访问 当前对象 的其他构造方法
注意:
1.this(...)必须是构造方法中第一条语句
2.不能形成环(比如,两个构造方法不能同时使用this() )
3.this() 只能用于构造方法内部,不能用于普通方法或静态方法中
默认初始化
通过前面的学习 我们知道了1.局部变量不进行初始化 会编译失败2.成员变量不进行初始化 会有默认值(取决于变量的类型) 那这又是为什么呢?
要解决这个问题 就需要知道new关键字背后所发生的一些事情:
Data d1=new Data(2008,8,8);
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
- 检测 对象 对应的类 是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
4.初始化所分配的空间
即:对象空间被申请好之后 对象中包含的成员已经设置好了初始值
5.设置对象头信息(关于对象内存模型后面会介绍)
6. 调用构造方法,给对象中各个成员赋值
就地初始化
在声明成员变量时,就直接给出了初始值
注意:代码编译完成后,编译器会将所有给成员变量初始化的这些语句添加到各个构造函数中
对于面向对象的语言来说 有几个比较重要的特性:封装 继承 多态
封装
什么是封装?封装的作用/意义是什么?
举例:手机 我们是看不到任何的内部实现细节的
但手机会留一些公开的 接口供我们使用
从语言上来说 我想达到同样的目的 只能通过对类进行封装 把细节隐藏起来 提供一些公开的可以访问的内容即可(对外隐藏 类 内部的实现细节)
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
访问限定符
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
比如:
public:可以理解为一个人的外貌 谁都可以看到
private:只有自己知道自己的秘密 其他人不知道
default:对于同一个家族不是什么秘密 对于其他人就是隐私了
在这里我们还不清楚包、子类是什么?
后面会有详细介绍
可以先将 同一包中 理解为 当前类
说明:
1.protected主要是用在继承中,继承部分详细介绍
2.default权限指:什么都不写时的 默认权限
3.访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
对成员变量 或成员方法 使用private进行修饰
达到的效果:被private修饰的成员变量/成员方法 此时只能在当前类当中使用 不能在当前类外使用
举例:
接下来我们开始学习什么是包、什么是子类
封装扩展之包
包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可
可以把包简单理解为文件夹
导入包中的类
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date 类.
但是这种写法比较麻烦一些, 可以使用 import语句导入包
如果需要使用 java.util 中的其他类, 可以使用 import java.util.*
把Date换成*后 就可以使用其他类了 并不局限于Date类
但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况
比如:
在这种情况下需要使用完整的类名
可以使用import static导入包中静态的方法和字段
注意事项: import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要. import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using
常见的包
1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
2. java.lang.reflflect:java 反射编程包;
3. java.net:进行网络编程开发包。
4. java.sql:进行数据库开发的支持包。
5. java.util:是java提供的工具程序包。(集合类等) 非常重要
6. java.io:I/O编程开发包。
自定义包
基本规则
1.在文件的最上方加上一个 package 语句指定该代码在哪个包中.
2.包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
3.包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码
4.如果一个类没有 package 语句, 则该类被放到一个默认包中
操作步骤:
1.在IDEA中先建一个包:右键src-->新建-->包
2.在弹出的对话框中输入包名, 例如 com.baidu.www
3. 在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可
4.此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了
5.同时我们也看到了, 在新创建的 Test.java 文件的最上方, 就出现了一个 package 语句
包的访问权限控制举例
static成员
我们以学生类为例:
初始化两个对象 每个对象都有特有的名字、年龄、学号....
假如两个对象是同一个班级 如下图:
我们在Student类中定义的成员变量 在每个对象中都会包含一份(称为实例变量) 因为需要这些信息来描述具体的学生
而当我们现在在描述同一个班级的学生时 这时候需要使用对className进行一个操作,
用static修饰 这样我们就不用对每个学生对象都一一进行变量的赋值(“1班”)
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对
象,是所有对象所共享的。
static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的(因此在一个对象中对静态成员变量的修改将影响其他所有对象)
静态成员变量特性
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
访问形式:
1.通过对象的引用访问(需要先实例化对象s1)
s1.className=”1班”;
2.通过类名访问(不需要实例化对象)
Student.className=”1班”;//更推荐通过类名访问
注意:静态的成员变量 也被叫做类变量 也就是说,这个变量属于类 而不是对象的(虽然都可以访问)
记住一句话:静态的 不依赖于对象。 意味着 可以不用对象的引用来访问
关于static 我们来看个题目:
这题能让我们更好的了解到 static修饰的成员变量--》静态成员变量 在一个对象中对静态成员变量的修改将影响其他所有对象 (count存在方法区中)
static修饰成员方法
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何在类外访问呢?
我们这里一个Student类 一个Test类
我们可以看到在Test类中 我们无法访问Student类中static修饰的className
那static属性应该如何访问呢?
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的
静态方法特性
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 在静态方法当中 不能够直接调用 非静态方法
因为:静态方法 不依赖于对象 可以直接通过类名进行访问
但是:非静态方法 依赖对象 需要通过对象的引用访问
所以,在静态方法中 若想调用非静态方法 需要实例化对象 才可使用非静态方法
在Java中 我们可以看到main函数中也有static--》静态方法 所以我们在使用非静态方法时 记得实例化对象
4.static方法当中 不能使用this关键字
5.不能在静态方法中访问任何非静态成员变量
总结:
成员方法或成员变量的调用就2种情况
- 要么通过对象的引用调用
- 要么通过类名调用
在静态方法中 不能直接使用任何非静态的成员变量和成员方法
要想使用 要通过new关键字 实例化对象 然后通过对象的引用访问
但是 在非静态方法当中 可以直接使用静态的成员方法或成员变量
把握住一点,静态的 不依赖于对象 属于类 不属于对象!
static成员变量初始化
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
关于静态成员变量的初始化
- 就地初始化
- 通过get set方法初始化
- 构造方法(很少)
- 静态代码块
就地初始化
就地初始化指的是:在定义时直接给出初始值
静态代码块初始化
那什么是代码块呢?我们继续往后看
代码块
代码块概念及其分类
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块 :定义在方法中的代码块
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
这种用法比较少见
构造代码块/实例代码块/非静态代码块
构造代码块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
补充:在没有继承关系(之后会讲)时的执行顺序
运行结果:
我们看到在这里 先执行了实例代码块 然后初始化对象
这表明 实例代码块在构造方法之前执行
静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
运行结果:
这里我们知道先执行静态代码块 次之为实例代码块 最后为构造方法
假如我这里给两个实例化对象呢
如:
结果还会是上面的重复吗?
其实不是
运行结果:
这是因为 静态的只会执行一次 因为 这个类只会加载一次(知识点:双亲委派模型——之后讲解)
结论:
静态代码块一般用来初始化静态成员变量
执行顺序:静态代码块>实例代码块>构造方法
如果代码块都是同一类的(除构造方法) 和定义顺序有关
注意事项
1.静态代码块不管生成多少个对象,其只会执行一次
2.静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
4.实例代码块只有在创建对象时才会执行 实例代码块执行完成后 最后构造方法执行
同步代码块(后续讲解多线程部分再谈)
内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
这里我们先不对内部类进行讲解 待我们学习完继承和多态 会再进行解读