目录
前言:
什么是面向对象?
什么是类?
类和方法的关系:
方法的定义:
方法重载:
类的定义:
修改类的名字:
实例化:
利用方法对其属性赋值:
this关键字:
this的意义:
this的使用:
对象的构造以及初始化:
非构造方法初始化:
构造方法:
this调用构造方法:
构造方法特性:
打印对象:
总结:
Java的特色就是面向对象,对象的上面是什么?对,就是类,这一章,我们就来仔细讲一讲类。
什么叫面向对象?什么叫面向过程?(如果你只学过C语言,那么说你之前写的代码都是面向过程)其实这两个概念也不用一直去纠结。比如把大象装进冰箱。
面向对象就会1.打开冰箱2.塞进大象3.关上冰箱。
而面对过程1.如何打开冰箱?2.如何塞进大象?3.如何关上冰箱?
面向对象需要:找对象 创建对象 使用对象。
这其实是一个抽象的概念,类似于C语言中的结构体。我们一般使用的都是基本数据类型(char、double、int之类的),但是定义一本书该怎么办?和C语言一样,需要自定义一种类型,之后一个具体的类叫做对象。
打个比方,我们老师,同学,亲人等等,这些人。你会发现他们都是人,同属于一个类,这个类就是人。他们都有具体的一些属性:姓名,年龄等等。当这些属性有了具体的数据,比如:张三,18岁。这个张三就是一个具体的类了,可以把他称作为-对象。
此时你就会理解什么是类了,类和对象的关系是什么。
我们知道每个人都是人类,都是人这个类的对象,那么人就都有一些共同的做法。比如吃饭,睡觉。
动物也是类,都有睡觉,大叫的做法。
我们把一个类的共同做法叫做方法,在类中定义,是一种所属关系。
在Java中,方法必须在类里面,方法不能嵌套方法(不能嵌套定义)。在Java中没有方法声明这一说。
Java方法也会开辟空间(就是方法的栈帧,这里详情请看函数的栈帧-CSDN博客)。
Java中对方法有一种神奇的用法,会方便我们去调用方法。
此时我们要求和,分别是対整数和浮点数进行求和,如果都写成方法,就要两种方法,因为类型不同。
public static int addInt(int a, int b){
return a + b;
}
public static double addDouble(double a, double b){
return a + b;
}
public static void main(String[] args) {
int x = 10;
int y = 20;
int ret = addInt(x, y);
System.out.println(ret);
double d1 = 12.3;
double d2 = 12.3;
double sum = addDouble(d1, d2);
System.out.println(sum);
}
这种方式比较冗余,都是实现加法,但是要调用两种方法,所以就衍生出:方法重载。
Java中允许存在相同名称的方法,但是要求返回值、参数列表不同。
所以此时我们就可以将以上代码修改为:
public static int add(int a, int b){
return a + b;
}
public static int add(int a, int b, int c){
return a + b;
}
public static double add(double a, double b){
return a + b;
}
public static void main(String[] args) {
System.out.println(add(1, 3));
System.out.println(add(1.7, 1.5));
System.out.println(add(1, 2, 3));
}
所以Java中定义: 如果多个方法的名字相同,参数列表不同,则称这几种方法被重载了。
那么java中到底如何区分呢?有一个东西叫做方法签名,就是经过编译器修改过之后方法最终的名字。
所以完整方法名:方法路径名 + 参数列表 + 返回值类型。
我们也可以看具体细节,先编译生成字节码class文件后,在控制台中进入到要查看的.class所在目录,输入:javap -v 字节码文件名字即可。
我认为看到这里没几人会去实际操作吧,哥哥们。这个只是让你们看到具体的方法名,没必要去实操,但是还是肯定会有的,因为我给的步骤不全,可能有些人就划走了。
所以我我给出具体步骤(这里我就不给出我写的另一个博客链接了,就这一点不跳转了),打开当前.java文件目录,在当前.java路径中输入cmd,之后先编译,输入javac 文件名.java,之后生成了字节码文件(.class文件)。
(上方是参考图,不是这个文件)之后按照那个输入即可。
这个了解即可。
有了以上基础,我们就来看看类是如何定义的?
class ClassName{
field; //字段(属性) 或者成员变量
method; //行为 或者 成员方法
}
类名注意采用大驼峰定义,成员前写法统一为public(以后会讲解),此处写方法不带static关键字(以后讲解)。
class Person{
//属性 字段 成员变量 : 定义在类当中 方法外部的变量
//public 访问修饰限定符
public String name;
public int age;
//静态成员变量
public static int count = 10;
//方法 行为
public void Sleep(){
}
//静态成员方法
public static void staticMethod(){
}
}
每一个类, 都会产生一个字节码文件(一般最好是这个类前面有public修饰,因为一个文件只能有一个public修饰的类),我们这里为了方便讲解,在一个文件中创建了多个类,一般建议一个文件中一个类。
此时我们先执行以下代码:
class Person{
//属性 字段 成员变量 : 定义在类当中 方法外部的变量
//public 访问修饰限定符
public String name;
public int age;
//静态成员变量
public static int count = 10;
//方法 行为
public void Sleep(){
}
//静态成员方法
public static void staticMethod(){
}
}
//class ClassName{
// field; //字段(属性) 或者成员变量
// method; //行为 或者 成员方法
//}
public class test {
public static void main(String[] args) {
int a = 10;//局部变量
}
}
class PetDog{
public String name;//名字
public String color;//颜色
//狗的属性
public void barks(){
System.out.println(name + "汪汪");
}
}
并返回上一级目录点击out,进到最里面会发现生成了好几个class文件。
由此可得,一个类就会生成一个.class文件。 不建议一个文件中有多个类,建议一个类放在一个文件当中,这样好管理。
public修饰的类必须要和文件名相同!而且main方法所在类一般要使用public修饰。
因为我们建议一个文件中一个类,所以不能只通过修改类的名字来进行重命名,不能直接修改,要借助工具。比如我们修改test类的名称。
但是这只针对一个文件中有一个类,当一个文件中有多个类,这样改就会报错。
和C语言一样,有匿名结构体,也有匿名类和对象。
匿名对象缺点:只能使用一次,不能重复使用。
public class Again {
String s;
public static void main(String[] args) {
//Again a = new Again();
//System.out.println(a.s);
//或者 创建一个匿名类
System.out.println(new Again().s);
}
}
将类进行实例化,其实也就是创建了一个对象,但是这个对象的赋值一开始不能像C语言一样赋值。我们先往下看:
//实例化 -> 就是实例化一个真正的实体
PetDog dog1 = new PetDog();
//通过 new 可以实例化多个对象
PetDog dog2 = new PetDog();
PetDog dog3 = new PetDog();
所以创建的对象也是引用变量。此时我们对其每一个成员变量进行赋值:
public class test {
public static void main(String[] args) {
//实例化 -> 就是实例化一个真正的实体
PetDog dog1 = new PetDog();
//赋值
dog1.name = "lele";
dog1.color = "black";
System.out.println(dog1.name);
dog1.barks();
int a = 10;//局部变量
}
}
class PetDog{
public String name;//名字
public String color;//颜色
//狗的属性
public void barks(){
System.out.println(name + "汪汪");
}
}
使用.来访问对象中的属性和方法。
类里面的方法是在方法区存放的,我们以后详解。
我们创建一个方法进行快速赋值:
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 static void main(String[] args) {
Date date = new Date();
//date.day = 11; 我们先不进行这样赋值
date.setDay(1999,1,11);
Date date1 = new Date();
date1.setDay(1899,2,3);
}
}
注意上面有两个对象创建成功了,都调用了一个方法,方法为了明确到底对谁进行赋值,我们知道是前面使用的对象调用的方法,所以其实有隐藏参数。
所以为了更好的区分,我们在方法中使用属性,前面都加上this.
public void setDay(int y, int m, int d){
//相当于一个方法
this.year = y;
this.month = m;
this.day = d;
}
我们先举一个例子:
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 print(){
System.out.println("year:" + year + "month:" + month + "day" + day);
}
public static void main(String[] args) {
Date date = new Date();
//date.day = 11; 我们先不进行这样赋值
date.setDay(1999,1,11);
date.print();
Date date1 = new Date();
date1.setDay(1899,2,3);
date1.print();
}
}
因为局部变量优先,所以要加上this,这样就能更好的区分。
public void setDay(int year, int month, int day){
//相当于一个方法
this.year = year;
this.month = month;
this.day = day;
}
this只能引用当前对象,不能引用其他对象。所以我们要习惯使用this。方法里出现属性也要加上this。
public void print(){
System.out.println("year:" + this.year + " month:" + this.month + " day" + this.day);
}
this可以后面跟上成员变量,也可以跟上成员方法(就是当前对象调用了方法)。
class PetDog{
public String name;//名字
public String color;//颜色
public void eat(){
System.out.println(this.name + "吃饭");
}
//狗的属性
public void barks(){
this.eat();
System.out.println(name + "汪汪");
}
}
我们只能针对每一个属性进行初始化。
PetDog dog1 = new PetDog();
dog1.name = "xiaole";
dog1.color = "red";
//构造对象
我们也可以进行另一种初始化(就地初始化)。
class PetDog{
//就地初始化
public String name = "lele";
public String color = "red";
public void eat(){
System.out.println(this.name + "吃饭");
}
//狗的属性
public void barks(){
this.eat();
System.out.println(name + "汪汪");
}
}
public class day_12_27 {
//静态成员方法
public static void main(String[] args) {
PetDog dog1 = new PetDog();
}
}
一般情况下有特殊业务需求才会使用。 也可以重新赋值,就好像修改变量一样。局部变量使用时一定要进行初始化,但是成员变量(属性)不初始化是默认赋值的。引用类型默认值都是null。
构造方法:一个非常特殊的方法!没有返回值,方法名必须和类名是一样的,当然也需要使用this。
public Student(){
//初始化成员变量
this.name = "daniu";
this.age = 9;
}
但是我们之前没有使用过构造方法,是不是意味着构造方法可有可无?我们可以发现,我们没有去调用构造方法,但是它却将对象赋值初始化了,也就是说,构造方法是默认调用的。
其实,对象产生时(实例化对象时),有两部很关键:为对象分配空间,调用合适的构造方法。
当我们没有写任何构造方法时,Java会提供一个默认构造方法。
public Student(){
}
如何验证,很简单,就是我们没有进行初始化时,默认都是0和null值。
所以没有构造方法时,编译器就默认帮你补上构造方法,写了就没有默认构造方法。我们刚才说,合适的构造方法,所以就存在多种构造方法。
class Student{
public String name;
public int age;
//构造方法,接收参数
public Student(String name, int age){
this.name = name;
this.age = age;
}
//构造方法
public Student(){
//初始化成员变量
this.name = "daniu";
this.age = 9;
}
public void show(){
System.out.println("姓名:"+this.name+" 年龄:"+this.age);
}
}
public class day_12_27 {
public static void main(String[] args) {
Student s = new Student();
Student s1 = new Student("zhangsan", 9);
//直接使用构造方法实例化对象
s.show();
s1.show();
}
}
此时发现代码报错,但是编译器不是能自动提供一个无参构造器吗?但是此时有构造器了。如果你没有写构造器,就默认会有无参构造器。但一旦写了一个,无参构造器没就没了。 所以叫做救急不救穷。
但是有多个成员变量时,我们写构造器就会有些麻烦,难道要把所有参数都写一遍吗?其实IDEA提供了快捷方式(Alt + Insert)或者鼠标右击点击generate,并把全部成员选中即可。
此时就快捷生成了构造器,生成时我们可以进行选择,生成有参还是无参的构造器。
this可以调用方法,那么构造方法也可被this调用,比如:
this("鼠标",9);
不能自己调用自己(this();),且必须出现在当前构造方法的第一行,否则报错。
所以有些地方说this代表当前对象是不正确的。
名字必须和类名相同,没没有返回值类型,设置void也不行。创建对象时编译器自动调用,并且在对象的生命周期内只调用一次。构造方法可以重载。我们也可以验证:
class Student{
public String name;
public int age;
public Student() {
//调用其他构造方法 -> 只能在构造方法中写
this("鼠标",9);
System.out.println("gaga");
}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("haha");
}
public void show(){
System.out.println("姓名:"+this.name+" 年龄:"+this.age);
}
}
public class day_12_27 {
public static void main(String[] args) {
Student s1 = new Student();
s1.show();
}
}
这里一定要注意,不能形成环。
public Date(){
this(19,1,1);
}
public Date(int y,int m, int d){
this();
}
有没有快捷打印对象每个成员的方法呢?我们如果直接打印就只会打印地址。
public static void main(String[] args) {
Student s1 = new Student("an",20);
System.out.println(s1);
}
我们进入println源码去看,一直点进去可以发现一个熟悉的方法toString。
也就是说,我们可以在类里面去使用toString方法打印每一个成员。 所以我们还是右击generate生成toString方法(相当于重写toSring方法了)。
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
成员属性/成员变量:定义在方法外部,类内部的变量。