构造方法
接上篇,若每次都想下面的setDate方法给对象初始化,未免比较麻烦,那有什么方法可以让初始化更加简便呢?
public void setDate(int year, int month, int day){this.year = year;this.month = month;this.day = day;}
答:使用构造方法
概念
构造方法是一个特殊的成员方法,名字必须与类名相同(没有返回值,设置为void也不行),在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
方法名 (形参列表){//方法名须与类名相同方法体
}
小拓展:一个对象生成至少有两步:
- 为对象分配内存
- 调用合适的构造方法
构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间,实例化时可以用构造方法对对象进行初始化
public Date(int year, int month, int day){this.year = year;this.month = month;this.day = day;}
对日期类可以做以上构造方法,这样在实例化时就可以直接为对象进行初始化,初始化代码如下
Date date = new Date(2004,12,20);
在写构造方法时可以发生重载:下面代码中即使两个构造方法的类名一样,但是参数不同,不会报错,发生了方法的重载,想要调用哪个就可以调用哪个。
但是当没有写任何构造方法时,Java会提供一个默认不带参数的构造方法(一旦写了构造方法,Java就不会在提供不带参数的构造方法),这也是我们不写构造方法对象也能生成的原因。
public Date(){System.out.println("这个是不带参数的构造方法");}public Date(int year, int month, int day){System.out.println("这个是带有三个参数的构造方法");this.year = year;this.month = month;this.day = day;}
构造方法中可以使用this来调用其他构造方法:
注意事项:
- this( ) 必须是该构造方法中的第一条语句
- 格式是
this( 参数 )
- 不能形成环(像循环一样调来调去的没有意义)
以下是在没有参数的Date构造方法中调用有参数的Date构造方法代码
public Date(int year, int month, int day){System.out.println("这个是带有三个参数的构造方法");this.year = year;this.month = month;this.day = day;}public Date(){this(2024,5,5);System.out.println("这个是不带参数的构造方法");}
总结:
- 构造方法名必须与类名相同
- 构造方法没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(实例化对象时一定会调用构造方法)
- 构造方法可以重载(可以根据需求写出不同的构造方法)
- 如果用户没有显式定义(自己写),编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的(注意:一旦用户定义,编译器则不再生成)
- 当然,当我们自己写的构造方法参数列表与实际实例化时参数列表个长度不同,由于编译器不会再生成不带参数的构造方法,所以编译器报错
- 绝大多数用public修饰,特殊情况下用private修饰
- 小技巧:idea中自动生成构造方法
封装
面向对象程序三大特性:封装、继承、多态。(可能的误区:这三个不是哪个语言的特征,而是面向对象的特征)
什么是封装?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
(套壳屏蔽细节,用东西包装起来对外隐藏内部实际细节)
总的来说就是对成员变量和成员方法用private进行修饰。
达到的效果:就是该成员变量或成员方法只能在当前类内部使用,当前类外不能使用(可以写一个方法只能供这个类内的方法使用,这样类外就不知道这个只能供这个类内的方法是怎么写的, 实现了隐藏)
访问限定符(管当前修饰的字段或方法的访问权限)
当一个字段或方法前没有任何访问限定符,则为默认权限 default
包
包的概念
在我们的电脑中,我们为了分类,会把同一类的东西放到一个文件夹中,但是我们会发现一个文件夹中不能出现相同名字的文件或文件夹。
类似的,在Java中为了更好地管理类,把类聚集到一个包中,称为软件包。包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,像文件夹一样,一个包中不能有名字相同的两个类(但是在一个工程中可以有两个名字相同的类,只要不在同一个包中)
导入包
Java 中已经提供了很多现成的类供我们使用,我们可以使用 import语句导入包比如说导入 java.util 这个包中的 Date 类
import java.util.Date;
如果想导入 java.util 这个包中的所有类可以 import java.util.*
但是同一个包中可能会有名字相同的类,所以建议显式的指定要导入的类名,否则还是容易出现冲突的情况
import导入的是包下的所有类,用到这个包底下哪个类就回调用哪个(而不是导入的这个包)
/*java.lang:系统常用基础类(String,Object),此包从JDK1.1后自动导入
java.lang.reflect:反射编程包
java.net:进行网络编程开发包
java.sql:进行数据库开发的支持包
java.util:Java提供的工具程序包(集合类等)非常重要
java.io:I/O编程开发包*/
可以使用import static导入包中静态的方法和字段
import static java.lang.Math.*
用静态导入包的好处是可以直接使用静态成员而不用通过类名来访问,使代码更简洁
注意事项: import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要。import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using
static
static修饰成员变量
static修饰的成员变量,称为静态成员变量,也叫类变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的
【静态成员变量特性】
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量只有一份且存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载(.class文件通过java文件转化成class对象)而创建,随类的卸载而销毁)
- Java当中不允许定义局部的静态变量(可以通过类名或通过对象的引用访问,建议用类名访问)(static修饰的变量是类变量而不是局部变量)
static修饰成员方法
被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的
【静态方法特性】
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过==类名.静态方法名(…)==方式调用,更推荐使用后者
- 不能在静态方法中访问任何非静态成员变量
- == 静态方法中不能直接调用任何非静态方法,非静态方法中可以调用静态方法==,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用(静态方法中不能使用 static 关键字)
因为:静态方法不依赖与对象,可以直接通过类名来访问
但是:非静态方法依赖于对象,要通过对象引用访问
想要在静态方法中调用非静态方法需要先new对象,在通过对象引用访问- 静态方法无法重写,不能用来实现多态
注意:静态的不依赖于对象,属于类,不属于对象
static成员变量初始化
一般通过四种方式:
就地初始化
静态代码块初始化(下面讲)
构造方法(少)
get set 方法初始化
代码块
概念及分类
使用 {} 定义的一段代码称为代码块。分下面四种:
- 普通代码块:定义在方法中的代码块(一般不写)
- 构造代码块:也叫实例代码块
- 静态代码块:一般用来初始化静态成员变量
static { }
- 同步代码块:未来再议
构造代码块:
//实例代码块/构造代码块{this.name = "z";//实例化对象this.age = 18;this.num = 11;}
静态代码块:
//静态代码块static {classname = 1;//静态成员变量}
关于代码块:
- 静态代码块在类加载的时候被执行(静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的)
- 如果定义了多个静态代码块/构造代码块,则执行顺序与定义顺序有关
- 静态代码块不管生成了多少个对象只会执行一次->说明这个类只会被执行一次
- 实例代码块只有在创建对象时才会执行
关于代码块顺序问题:
由于静态代码块在加载时就执行了,而实例代码块在创建对象时才会执行,所以静态代码块的执行顺序先与实例代码块
//实例代码块/构造代码块{this.name = "z";this.age = 18;this.num = 11;System.out.println("构造");}//静态代码块static {classname = 1;System.out.println("静态");}//构造代码块{this.name = "z";this.age = 18;this.num = 11;System.out.println("构造");}
上述代码中,打印顺序为下图,由此可见静态代码块执行顺序先与构造代码块
对象的打印
重新实现toString来打印对象,代码如下
public class Person {public String name;public String gander;public int age;public Person(String name, String gander, int age){//构造方法this.name = name;this.gander = gander;this.age = age;}public String toString(){//重写toStringreturn "[" + this.name + "," + this.gander + "," + this.age + "]";}public static void main(String[] args) {Person person = new Person("z","女",13);System.out.println(person);//打印时参数为对象名}
}