类和对象详解

编程入门 行业动态 更新时间:2024-10-24 04:49:42

类和对象<a href=https://www.elefans.com/category/jswz/34/1770044.html style=详解"/>

类和对象详解

1.类和对象的概念

类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性,哪些功能(能干什么)。
比如:

狗,它是一个动物类,在Java中可以将其看成是一个类别。
属性:名字,颜色,年龄...
干什么:叫,吃饭,睡觉,玩....

2.类和对象的定义与使用

1.定义类的语法 

class ClassName {

        field //字段(属性)或者 成员变量

        method //行为或成员的方法
}

其中class是定义类的关键字,ClassName为类的名字,要用大驼峰的方式来写即每个单词的首字母都要大写,{ }中为类中的主体。  

2.创建对象的语法

格式一:声明并实例化对象

类名称 对象名称=new 类名称();

格式二:先声明对象,然后实例化对象

类名称 对象名称=null;

对象名称=new 类名称(); 

例: 

class Dog{//创建一个狗类public String color;public String name;public int age;public void barks(){System.out.println(name+":汪汪汪~");}public void wag(){System.out.println(name+":摇尾巴~");}}
public class J1103 {public static void main(String[] args) {Dog dog1 = new Dog(); //通过new实例化对象dog1.name = "阿黄";dog1.color = "黄";dog1.barks();dog1.wag();Dog dog2 = null;//这里代表dog2这个引用不指向任何对象dog2=new Dog();dog2.name = "阿黑";dog2.color = "黑";dog2.barks();dog2.wag();}
}

定义了一个类,就相当于在计算机中定义了一种新的类型。
上述例子用类 类型创建对象的过程,称为类的实例化。

当实例化了以后,狗这个类就一个实体了,是某只叫阿黄的黄狗,会汪汪汪和摇尾巴。 

运行结果: 

关于类的注意事项:

1. 一般一个文件当中只定义一个类
2. main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
3. public修饰的类必须要和文件名相同
4. 不要轻易去修改public修饰的类的名称,如果要修改,就这样修改:

3.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 d3Date d1 = new Date();Date d2 = new Date();Date d3 = new Date();
// 对d1,d2,d3的日期设置d1.setDay(2020, 9, 15);d2.setDay(2020, 9, 16);}}

 以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打印,代码整体逻辑非常简单,没有任何问题。


但是细思之下有以下两个疑问:
1. 形参名不小心与成员变量名相同:

public void setDay(int year, int month, int day){year = year;month = month;day = day;
}

 2. 三个对象都在调用setDate和printDate函数,但是这两个函数中没有任何有关对象的说明,setDate和printDate函数如何知道打印的是那个对象的数据呢?

这时候就需要this引用来发挥作用了 。

 1.什么是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);}
}

 注意:this引用的是调用成员方法的对象。

public static void main(String[] args) {Date d = new Date();d.setDay(2020,9,15);d.printDate();
}

现在知道了this的重要性,为了保险起见,无论当前代码需不需要用this,我们都应当主动在能加的地方加上this。

2.this引用的特性

1. this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
2. this只能在"成员方法"中使用
3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
 

4.对象的构造及初始化

4.1 构造方法的概念及作用 

1.构造方法:(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。

2.作用:用来给成员变量进行初始化,也就是产生对象

3.使用new关键字产生一个对象时,大致分为以下两步:

(1) 为对象分配内存(空间大小由该类中成员变量的属性决定)

(2) 调用合适的构造方法来初始化当前对象中的成员变量

4.2构造方法特性:

1. 名字必须与类名相同
2. 没有返回值类型,设置为void也不行
3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法) 

 例:

public class Date {public int year;public int month;public int day;// 无参构造方法public Date() {this.year = 1900;this.month = 1;this.day = 1;}// 带有三个参数的构造方法public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public void printDate(){System.out.println(year + "-" + month + "-" + day);}public static void main(String[] args) {Date d = new Date();d.printDate();}
}

上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载

 5.一个类中至少存在一个构造方法,如果用户没有显式定义,编译器会生成一份默认的构造方法(编译器不会显示出来),生成的默认构造方法一定是无参的

public class Date {public int year;public int month;public int day;public void printDate(){System.out.println(year + "-" + month + "-" + day);//这就是编译器默认生成的无参构造方法,不会显示出来/*public Date(){}*/}public static void main(String[] args) {//当创建对象时默认调用该类的构造方法Date d = new Date();d.printDate();
}
}

6.如果用户自己定义了构造方法,编译器则不再生成。

public class Date {public int year;public int month;public int day;public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public void printDate(){System.out.println(year + "-" + month + "-" + day);}public static void main(String[] args) {// 如果编译器会生成,则生成的构造方法一定是无参的// 则此处创建对象是可以通过编译的// 但实际情况是:编译器报错Date d = new Date();d.printDate();}
}/*
Error:(26, 18) java: 无法将类 extend01.Date中的构造器 Date应用到给定类型;
需要: int,int,int
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
*/

7. 构造方法中,可以通过this调用其他构造方法来简化代码

public class Date {public int year;public int month;public int day;// 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复// 此处可以在无参构造方法中通过this调用带有三个参数的构造方法// 但是this(1900,1,1);必须是构造方法中第一条语句public Date(){//System.out.println(year); 注释取消掉,编译会失败this(1900, 1, 1);//this.year = 1900;//this.month = 1;//this.day = 1;} ///带有三个参数的构造方法public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}
}

不过这么做有两点需注意:
1.this(...)必须是构造方法中第一条语句
2.不能形成环,否则构造方法会死循环

public Date(){this(1900,1,1);
}
public Date(int year, int month, int day) {this();
} /* 
报错:
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:Error:(19, 12) java: 递归构造器调用
*/

4.3 构造方法的初始化

构造方法的初始化一种有两种方式:默认初始化就地初始化

1.默认初始化

public class Date {public int year;public int month;public int day;public Date(int year, int month, int day) {// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?System.out.println(this.year);System.out.println(this.month);System.out.println(this.day);}public static void main(String[] args) {// 此处a没有初始化,编译时报错:// Error:(24, 28) java: 可能尚未初始化变量a// int a;// System.out.println(a);Date d = new Date(2021,6,9);}
}

要搞清楚这个过程,就需要知道 new 关键字背后所发生的一些事情

Date d = new Date(2021,6,9);

在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突

数据类型默认值
byte0
char'\u0000'
short0
int0
long0L
booleanfalse
float0.0f
double0.0
referencenull

4. 初始化所分配的空间

5. 设置对象头信息(本文暂不介绍)

6. 调用构造方法,给对象中各个成员赋值
 

2.就地初始化 

在声明成员变量时,就直接给出了初始值。

public class Date {public int year = 1900;public int month = 1;public int day = 1;public Date(){}public Date(int year, int month, int day) {}public static void main(String[] args) {Date d1 = new Date(2021,6,9);Date d2 = new Date();}
}

注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中

5.封装 

何为封装呢?简单来说就是套壳屏蔽细节

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

为什么要封装?

封装是出于对代码的安全性考虑

5.1 访问限定符

 Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

范围privatedefaultprotectedpublic
1同一包中的同一类
2同一包中的不同类
3不同包中的子类
4不同包中的非子类

public:公共的,在当前程序(项目)中都是可见并且可以使用的
default:默认的,对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了

protected:受保护的,继承访问权限
private:私人的,只在当前类可见,出了类{ },对外就完全隐藏了,外部不知道有其存在

这里主要说private这个访问限定符

class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void sleep(){System.out.println(this.name+"正在睡觉");}}
public class Test {public static void main(String[] args) {Student student=new Student("张三",10);//当name和age被private修饰后这么做会报错//student.name="李四";//student.age=18;//正确做法:student.setName("李四");student.setAge(18);System.out.println(student.getName());}
}

在上述的例子中name和age被private修饰,只能在Student类中被访问,不能在其他类中被访问。 

当在Test这个类通过student修改name和age时可以看到代码是报错的。 

应该通过定义getName和getAge方法,提供对name和age的访问以及修改。

可以看到此时代码不会报错了。 

这种方式实现了封装,能够确保name和age只能通过提供的方法进行访问和修改。

5.2 包

包的概念:

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。

包命名规则:

只能包含数字、字母、下划线和“.”,但是不能用数字开头,不能是关键字。通常会用公司的域名的颠倒形式

例:com.bit.demo1

包的使用: 

1.在Java源文件的第一行使用import语句导入包其他包中的类。例如:

import java.util.Date; 

这个语句表示我们要使用 java.util 包下的Date类,后面就可直接使用该类

2.不在第一行用import语句导入包中所需的类,在后面使用该类时这么写,此法较麻烦

java.util.Date date = new java.util.Date();

3.还可以导入一个类,让你想用什么类都无需额外写,只需导入这一个类就行s 

import java.util.*

但是在这里更建议直接的指定要导入的类名. 否则还是容易出现冲突的情况.

4.使用import static导入包中静态的方法和字段

例如: 

import static java.lang.Math.*;

 当导入了上述的方法,那么下面这句

double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

就可以这么写:

double result = sqrt(pow(x, 2) + pow(y, 2));

可以看到静态导入的方式写起来更方便一些 

如何创建一个包: 

 

2. 在弹出的对话框中输入包名, 例如 com.bit.www
 

 

 3. 在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可.

在创建好包之后,在包中创建的类中最上面会有一个package语句来告诉你这个类属于哪个包

 

注意:在不同包中建相同名称的类是不冲突的,但在相同包下创建 相同名称的类是冲突的。
 

常见的包:

 1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
2. java.lang.reflect:java 反射编程包;
3. java:进行网络编程开发包。
4. java.sql:进行数据库开发的支持包。
5. java.util:是java提供的工具程序包。(集合类等) 非常重要
6. java.io:I/O编程开发包
 

6.static成员

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的

6.1static修饰成员变量

【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中

张三,李四,王五是一个班的,他们上课一定在同一教室上课,那么上课教室就是张三,李四,王五这个几个对像共享的 

class Student {private String name;private int age;//private String classRoom;public static String classRoom;public Student(String name, int age) {this.name = name;this.age = age;this.classRoom=classRoom;}public String getClassRoom() {return classRoom;}public void setClassRoom(String classRoom) {this.classRoom = classRoom;}public void sleep(){System.out.println(this.name+"正在睡觉");}
}
public class Test {public static void main(String[] args) {Student student1=new Student("张三",10);student1.setClassRoom("3-108");Student student2=new Student("李四",10);student2.setClassRoom("3-108");Student student3=new Student("王五",10);student2.setClassRoom("3-108");}
}

 变量classRoom被private修饰时:

 变量classRoom被static修饰时: 


2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问,因为static修饰的变量不属于某个具体的对象,虽然可以通过对象访问但这么做想想其实不合理

class Student {public static String name;public static int age;//private String classRoom;public Student(String name, int age) {this.name = name;this.age = age;//this.classRoom=classRoom;}public void sleep(){System.out.println(name+"正在睡觉");}
}
public class Test {public static void main(String[] args) {Student student=new Student("张三",10);
//        方法一:(不推荐)student.name="李四";student.age=18;student.sleep();//        方法二:(推荐)Student.name="王五";Student.age=19;student.sleep();}
}

运行结果:


3. 类变量存储在JVM方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

 6.2 static修饰成员方法

Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。

 【静态方法特性】
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者

class Student {public static String name;public static int age;public Student(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(this.name+"正在吃饭");}public static void sleep(){System.out.println(name+"正在睡觉");}
}
public class Test {public static void main(String[] args) {Student student=new Student("张三",10);//通过对象调用student.sleep();//通过类名.静态方法名(...)调用Student.sleep();}
}

3. 不能在静态方法中访问任何非静态成员变量

 4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用


 

6.3 static成员变量初始化

静态成员变量的初始化分为两种:就地初始化 静态代码块初始化

 1. 就地初始化
就地初始化指的是:在定义时直接给出初始值

public class Student{private String name="张三";private String gender="男";private int age=10;private double score=100.0;private static String classRoom = "Bit306";
// ...
}

2. 静态代码块初始化
那什么是代码块呢?继续往后看。


7. 代码块

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

  1. 普通代码块:定义在方法中的代码块
  2. 构造代码块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
  3. 静态代码块:使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
  4. 同步代码块(本文不涉及)
class TestStatic {public static int staticNum = 100;public int data1 = 10;{//构造代码块 || 实例代码块System.out.println("实例代码块被执行了....");data1 = 1000;//一般情况下  用来初始化 非静态的数据成员}static {//静态代码块:初始化静态成员变量。System.out.println("静态代码块被执行了....");staticNum = 9999;}static {staticNum = 91999;}public TestStatic() {System.out.println("执行构造方法.....");}public static void main(String[] args) {TestStatic testStatic1 = new TestStatic();System.out.println(testStatic1.data1);System.out.println(TestStatic.staticNum);System.out.println("--------------------");TestStatic testStatic2 = new TestStatic();{//普通代码块}}
}

 运行结果:

 

运行结果我们可以看出:1.执行顺序:静态代码块>实例代码块>构造方法 

                                        2 静态代码块不管生成多少个对象,其只会执行一次

注意事项: 

  • 静态代码块不管生成多少个对象,其只会执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  • 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
  • 实例代码块只有在创建对象时才会执行

更多推荐

类和对象详解

本文发布于:2024-03-04 01:47:41,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1707934.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:详解   对象

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!