莫道桑榆晚,为霞尚满天。
文章目录
- 面向对象的初步认识
- 面向对象与面向过程
- 什么是面向对象
- 类的定义和使用
- 类的定义格式
- 类的实例化
- 什么是实例化
- 类和对象的使用
- this引用
- 什么是this引用
- this 引用的特性。
- 对象的构造以及初始化
- 如何初始化对象
- 构造方法
- 首先第一:构造函数可以重载
- 第二:构造函数与构造函数之间是可以相互调用的但是不能形成死环并且格式是this(...)
- 默认初始化
- 封装
- 封装的概念
- 访问权限限定符
- 封装扩展之包
- 包的概念
- 导入包中的类
- 自定义包
- static成员
- static修饰的作用
- static修饰的成员变量
- static 修饰的成员方法
- static 修饰的成员变量初始化
- 代码块
- 代码块概念以及分类
- 构造代码块
- 静态代码块
面向对象的初步认识
面向对象与面向过程
我们计算机的编程有两种一种是面向对象的一种是面向过程的,那么我们平时在做算法题目的时候其实就是面向过程的,比如说我们要去洗衣服,那么面向过程的算法思路可能就是我第一步要接水第二步把衣服放进洗衣机里然后是第三步第四步,这个是面向过程,而面向对象与其不同的地方就是面向对象的编程它不强调过程而是强调不同对象之间的交互,就还是以洗衣服为例面向对象就是会创建一个类叫做洗衣机,在创建一个类叫做水龙头,然后依据两个类实例化出的对象进行交互。
什么是面向对象
Java是一门纯面向对象的语言(Object Oriented Program,简称OOP它主要是依靠对象与对象之间的交互解决问题而我们在生活中相信也听过一句话就是java不如c++适合做算法这其实也就是因为c++由于要保留c语言的特性这就导致了c++保留的有面向过程这个特性不是一个纯面向对象的语言因此c++更适合做算法题。
类的定义和使用
面向对象的编程主要关注的对象比如说一个洗衣机,但是一个洗衣机也有很多的属性和方法比如说洗衣服的模式就分为了很多种,并且还包括了很多的属性比如说洗衣机的大小啊品牌啊,各种方面的属性,因此我们可以使用面向对象的语言来描述一台洗衣机。
类的定义格式
class ClassName{
field; // 字段(属性) 或者 成员变量
method; // 行为 或者 成员方法
}
其实细心的朋友其实可以注意到我们的一个java文件其实就有一个public class fileName是的其实这也是一个类但是呢一个文件只能有一个public类以及public是什么我们后面再去讲解。在上面的那个代码块中呢class是类的关键字,className是类的名称。
注意事项
类名注意采用大驼峰定义
成员前写法统一为public,后面会详细解释
此处写的方法不带 static 关键字. 后面会详细解释
这里给大家举一个具体的例子吧比如说一个狗类那么代码如下
class Dog{
public int age;
public String name;
public String color;
public void DogBarking(){
System.out.println(name+"在汪汪叫");
}
}
类的实例化
什么是实例化
在介绍实例化之前我们先解释一下什么是实例化,我们现在理解的类是什么,如果把类放在现实中其实他代表的就是字面的意思也就是一个类别,比如说人类是一个类别,狮子是一个类别,那么听到这里你或许就了解了,人是一个类,而我们每一个个体就是这个类实例化出的一个对象,而我们生活中的各种行为其实就验证了面向对象的最核心的地方那就是对象与对象之间的交互从而去解决问题,用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象
代码如下还是以狗这个类为基础,
class Dog{
public int age;
public String name;
public String color;
public void DogBarking(){
System.out.println(name+"在汪汪叫");
}
}
public class Main{
public void main(){
Dog d=new Dog();//这里就是实例化
d.name="ww";//通过实例化出的对象可以去调用这个类的成员方法和变量
d.DogBarking();
}
}
注意事项
new 关键字用于创建一个对象的实例.
使用 . 来访问对象中的属性和方法.
同一个类可以创建对个实例.
类和对象的使用
- 这里呢大家要先明白一个概念就是类其实就只是一个模型,就像我们盖房子,类其实就是我们房子的设计图,而最终按照这个图纸盖出来的房子其实就是我们实例化出的一个对象。
- 此外呢一个类是可以实列化出多个对象的就像盖房子你可以按照一个图纸盖多个房子是一样的。
- 类是一种自定义类型可以用来定义变量。
- 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
this引用
先看一个日期类的例子:
public class Date {
public int year;
public int month;
public int day;
public void setDay(int y, int m, int d){
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
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();
}
}
以上代码呢非常的简单就是创建一个类再创建几个对象然后对对象进行赋值并且最后进行了打印,可是这里我们想一个问题那就是假如说我们赋值函数内的变量名称跟我们类的成员变量名称重合了怎么办呢?就是如下
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
那么这个时候我们可以发现打印出来是000这是为什么呢其实很简单是因为啊我们学习c语言的时候有一个局部优先规则,那么这里如果重复了的化year
那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量参数?估计
自己都搞不清楚了。 三个对象都在调用setDate和printDate函数,但是这两个函数中没有任何有关对象的说明,setDate和printDate函数如何知道打印的是那个对象的数据呢?一切让this引用来揭开这层神秘的面纱
public void setDay(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
什么是this引用
在回答这个问题之前呢我们要先知道这个this是从哪儿来的,其实在我们类的方法中呢默认的第一个参数就是this但是由于这是一个隐形变量,因此在成员函数中是默认不显示的。this引用指向当前的对象,(成员方法运行时调用该成员变量的方法),,在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
class Eg{
public int a;
public void Func(Eg this){
//这里的this变量也可以直接不写因为默认时会有的
this.a=10;
}
}
注意:this引用的是调用成员方法的对象。
this 引用的特性。
- this的类型对应类型引用,也就是说哪个对象调用就是哪个对象的引用。
- this只能在成员方法中使用
- this只能引用当前对象不能引用其他的对象
- this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法
对象的引用传递给该成员方法,this负责来接收
对象的构造以及初始化
如何初始化对象
通过前面的学习我们知道在java内部定义一个局部变量必须要初始化否则会编译报错。
public static void main(String[] args) {
int a;
System.out.println(a);
}
要让上述代码通过编译,非常简单,只需在正式使用a之前,给a设置一个初始值即可。如果是对象:那么就需要在使用之前呢先把需要的变量先进行赋值,通过上述的日期例子我们可以发现:
- 在每一次使用之前都需要先使用setDay方法对我们的日期进行赋值。
- 那么这里我们有个疑问为什么有时候类内的局部变量我们没有进行赋值却仍然可以使用呢?
这是因为类内成员的赋值其实是依赖构造函数进行的,而构造函数呢有一个特点就是如果说你自己不实现构造函数的化那么编译器就会默认的帮我们生成一个构造函数从而为你的成员变量进行赋初始值。那么说到这里我们就可以明白为什么有时候变量不赋值会出错但是我们却仍然需要去记住一些类型的变量的初始值是什么。
构造方法
概念:构造方法是用来初始化成员变量的一个方法,这个方法在类当中跟其他方法不一样首先就是这个方法没有返回值并且方法名称和类名称是一样的。并且呢构造方法在这个对象的生命周期中只能被调用一次在创建对象的时候编译器会自动调用,定义这个方法的时候格式如下(假设我们有一个MyClass类)
class Myclass{
public int m;
public int n;
public MyClass(){
m=10;
n=10;
}
}
5.2.2 特性
- 名字必须与类名相同
- 没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
其次呢跟大家说一下构造函数一些需要注意的地方
首先第一:构造函数可以重载
class Myclass{
public int m;
public int n;
public MyClass(){
m=10;
n=10;
}
public Myclass(int m_,int n_){
m=m_;
n=n_;
}
}
也就是说一个类当中允许有多个构造函数但要求是必须构成重载。
第二:构造函数与构造函数之间是可以相互调用的但是不能形成死环并且格式是this(…)
//正确的格式
class Myclass{
public int m;
public int n;
public MyClass(){
this(10,20);//
m=10;
n=10;
}
public Myclass(int m_,int n_){
m=m_;
n=n_;
}
}
//错误的格式1:死环
class Myclass{
public int m;
public int n;
public MyClass(){
this(10,20);//
m=10;
n=10;
}
public Myclass(int m_,int n_){
this();
m=m_;
n=n_;
}
}
//错误格式2:this调用
class Myclass{
public int m;
public int n;
public MyClass(){
this.MyClass(10,20);//此处必须为this(10,20)调用普通的成员变量和成员方法的时候才可以this.
m=10;
n=10;
}
public Myclass(int m_,int n_){
m=m_;
n=n_;
}
}
默认初始化
在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
这是因为啊,我们在实例化对象的时候用了一个new,这个new呢在我们用户看到的就是new一个内存但是其实在jvm中是要做很多事情的。具体的一些如下
- 首先要判断是否加载如果没有加载的话要进行加载
- 为对象分配一块内存
- 处理并发安全问题比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
- 初始化所分配的空间即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:int初始化为0之类的。
- 设置对象头信息(关于对象内存模型后面会介绍)
- 调用构造方法,给对象中各个成员赋值
封装
封装的概念
面向对象编程的三大特点就是封装,继承和多态而类和对象阶段我们主要研究的就是封装特性,那么什么是封装呢,其实就像字面意思一样就是对某个资源进行套壳使得我们想要让其他人使用的地方可以正常使用,不想被其他人使用的地方可以保护起来不让别人触碰,就像我们的手机,我们可以使用屏幕啊等一些裸漏的部件,但是呢一些封闭的部件是不能使用的。
由此我们可以得到封装的概念:封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
访问权限限定符
有了上面的概念之后呢我们就可以讲解一下接下来的这个知识了,上面说了我们的封装的概念其实就是隐藏我们想隐藏的让用户使用用户需要使用的。那么这个目的该怎么达到呢?其实就是通过访问限定符实现的,我们之前用的public其实就是访问限定符的一种那么在java中访问限定符有哪些呢?看下图
public:可以理解为一个人的外貌特征,谁都可以看得到
default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
private:只有自己知道,其他人都不知道
protected主要是用在继承中,继承部分详细介绍
default权限指:什么都不写时的默认权限
访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
public class Computer {
private String cpu; // cpu
private String memory; // 内存
public String screen; // 屏幕
String brand; // 品牌---->default属性
public Computer(String brand, String cpu, String memory, String screen) {
this.brand = brand;
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
public void Boot(){
System.out.println("开机~~~");
}
public void PowerOff(){
System.out.println("关机~~~");
}
public void SurfInternet(){
System.out.println("上网~~~");
}
}
public class TestComputer {
public static void main(String[] args) {
Computer p = new Computer("HW", "i7", "8G", "13*14");
System.out.println(p.brand); // default属性:只能被本包中类访问
System.out.println(p.screen); // public属性: 可以任何其他类访问
// System.out.println(p.cpu); // private属性:只能在Computer类中访问,不能被其他类访问
}
}
注意:一般情况下成员变量设置为private,成员方法设置为public
封装扩展之包
包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
导入包中的类
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.但是这种写法比较麻烦一些, 可以使用 import语句导入包.如果需要使用 java.util 中的其他类, 可以使用 import java.util.*但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况.
注意事项: import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要.
import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using
自定义包
基本规则
在文件的最上方加上一个 package 语句指定该代码在哪个包中.
包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储
代码.
如果一个类没有 package 语句, 则该类被放到一个默认包中.
static成员
好的那么接下来我们来讲解一下static成员,那么我们呢以学生类为例假设说一个学生类当中包含的属性有班级年龄,性别,姓名。
class student{
public String name;
public String cs;
public String gender;
public int age;
}
那么此时每当我们实例化出一个类的时候我们就相应的需要对这些字段进行赋值可是如果说我们的这些个同学是同一个班级呢?也就是说我们假设一个前提条件就是这个学生类实例化出来的学生对象是在同一个班级中的,那么如果我们还是对每一个学生都进行班级的赋值就会变得麻烦很多,也正因此我们可以使用static 修饰,那么采用static修饰有什么作用呢?
static修饰的作用
之前我们讲过类是一个图纸,实列化出的对象是盖好的房子而实例化出的对象呢会拥有我们类中的各种属性,但是static修饰的成员变量除外以学生类为列:Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量),因为需要使用这些信息来描述具体的学生。而现在要表示学生上课的教室,这个教室的属性并不需要每个学生对象中都存储一份,而是需要让所有的学生来共享在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
static修饰的成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
static 修饰的成员方法
static可以用来修饰成员变量那么static可以用来修饰成员方法吗?答案肯定是可以的,那么static修饰的成员方法除了上上面已经提到的一些不同之外还有什么不同呢?在一个类中如果一个方法被static方法修饰的话那么这个方法就是属于类的不是属于某个对象的因此他是没有this的,在Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
- 不能在静态方法中访问任何非静态成员变量
- 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
static 修饰的成员变量初始化
有两种一种是:就地初始化即在定义的时候直接初始化赋值
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
构造代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
public class Student{
//实例成员变量
private String name;
private String gender;
private int age;
private double score;
public Student() {
System.out.println("I am Student 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) {
Student stu = new Student();
stu.show();
}
}
静态代码块
静态代码块使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
//实例代码块
{
this.name = "bit";
this.age = 12;
this.gender = "man";
System.out.println("I am instance init()!");
}
// 静态代码块
static {
classRoom = "bit306";
System.out.println("I am static init()!");
}
public Student(){
System.out.println("I am Student init()!");
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
}
}
静态代码块不管生成多少个对象,其只会执行一次
静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
实例代码块只有在创建对象时才会执行