一、构造函数
1、构造函数的作用: 给对应的对象进行初始化。
2、构造函数的定义的格式:
修饰符 函数名(形式参数){
函数体...
}
3、构造函数要注意的细节:
① 构造函数是没有返回值类型的。
② 构造函数的函数名必须要与类名一致。
③ 构造函数并不是由我们手动调用的,而是在创建对应的对象时,jvm就会主动调用到对应的构造函数。
④ 如果一个类没有显式的写上一个构造方法时,那么java编译器会为该类添加一个无参的构造函数的。
⑤ 如果一个类已经显式的写上一个构造方法时,那么java编译器则不会再为该类添加一个无参的构造方法。
⑥ 构造函数是可以在一个类中以函数重载的形式存在多个的。
4、疑问:创建对象时,jvm就会调用到对应的构造方法,那么我们以前没有学构造方法,那么以前创建对象时,jvm是否 也会调用构造方法呢?如果有?构造方法从何而来呢?
答:会调用, java编译器在编译的时候给加上去的。
5、jdk提供了一个java开发工具(javap.exe)给我们进行反编译的。
javap 反编译工具的使用格式:javap -c -l -private 类名
6、疑问: java编译器添加的无参构造方法的权限修饰符是什么?
答:与类的权限修饰是一致的。
7、构造函数与普通函数的区别:
① 返回值类型的区别:
◆ 构造函数是没有返回值类型的,
◆ 普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。
② 函数名的区别:
◆ 构造函数的函数名必须要与类名一致,
◆ 普通函数的函数名只要符合标识符的命名规则即可。
③ 调用方式的区别:
◆ 构造函数是在创建对象的时候由jvm调用的。
◆ 普通函数是由我们使用对象调用的,一个对象可以对象多次普通的函数
④ 作用上的区别:
◆ 构造函数的作用用于初始化一个对象。
◆ 普通函数是用于描述一类事物的公共行为的。
8、需求: 使用java类描述 一个婴儿。在现实生活中有两种婴儿,一种婴儿一出生就具备了名字(白户),还有一种婴儿就是出生之后才有名字的(黑户)。
//婴儿类
class Baby{
int id; //身份证
String name; //名字
//构造函数
public Baby(int i , String n){
id = i;
name = n;
System.out.println("baby的属性初始化完毕!");
}
//无参的构造函数
public Baby(){
System.out.println("无参的构造函数被调用了..");
}
//哭
public void cry(){
System.out.println(name+"哇哇哭...");
}
}
class Demo8.1{
public static void main(String[] args){
//创建一个baby对象
Baby b1 = new Baby(110,"张三"); //婴儿诞生 白户
System.out.println("编号:"+ b1.id +" 姓名:"+ b1.name);
b1.cry();
//黑户
Baby b2 = new Baby();
b2.id = 112;
b2.name = "李四";
System.out.println("编号:"+ b2.id +" 姓名:"+ b2.name);
}
}
二、构造代码块
1、构造代码块的作用:给对象进行统一的初始化。
2、构造函数的作用: 给对应的对象进行初始化。
3、构造代码块的格式:
{
构造代码块
}
注意: 构造代码块的大括号必须位于成员位置上。
4、代码块的类别
① 构造代码块。
② 局部代码块。 大括号位于方法之内。 作用:缩短局部变量的生命周期,节省一点点内存。
③ 静态代码块 static
class Baby{
int id; //身份证
String name; //名字
//构造代码块...
{
System.out.println("构造代码块的代码执行了......");
}
public Baby(int i , String n){
id = i;
name = n;
}
public Baby(){
}
public void cry(){
System.out.println(name+"哇哇哭...");
}
}
class Demo8.2{
public static void main(String[] args){
Baby b1 = new Baby(110,"狗娃");
new Baby(112,"狗剩");
new Baby();
}
}
5、构造代码块要注意的事项:
① java编译器编译一个java源文件的时候,会把成员变量的声明语句提前至一个类的最前端。
② 成员变量的初始化工作其实都在在构造函数中执行的。
③ 一旦经过java编译器编译后,那么构造代码块的代码块就会被移动构造函数中执行,是在构造函数之前执行的,构造函数的中代码是最后执行的。
④ 成员变量的显示初始化与构造代码块的代码是按照当前代码的顺序执行的。
class Demo8.3{
//构造函数
public Demo5(){ //构造函数
i = 3;
}
//构造代码块 //构造代码块的初始化
{
i = 2;
}
int i = 1; //成员变量的显初始化
public static void main(String[] args){
Demo5 d = new Demo5();
System.out.println("i = "+d.i);
}
}
三、this关键字
问题:存在同名的成员变量与局部变量时,在方法的内部访问的是局部变量(java 采取的是就近原则的机制访问的)。
1、this关键字:this关键字代表了所属函数的调用者对象。
2、this关键字作用
① 如果存在同名成员变量与局部变量时,在方法内部默认是访问局部变量的数据,可以通过this关键字指定访问成员变量的数据。
② 在一个构造函数中可以调用另外一个构造函数初始化对象。
3、this关键字调用其他的构造函数要注意的事项:
① this关键字调用其他的构造函数时,this关键字必须要位于构造函数中的第一个语句。
② this关键字在构造函数中不能出现相互调用 的情况,因为是一个死循环。
4、this关键字要注意事项
① 存在同名的成员变量与局部变量时,在方法的内部访问的是局部变量(java 采取的是“就近原则”的机制访问的)
② 如果在一个方法中访问了一个变量,该变量只存在成员变量的情况下,那么java编译器会在该变量的前面添加this关键字。
class Animal{
String name ; //成员变量
String color;
public Animal(String n , String c){
name = n;
color = c;
}
//this关键字代表了所属函数的调用者对象
public void eat(){
String name = "老鼠"; //局部变量
System.out.println(this.name+"在吃..."); //需求:就要目前的name是成员变量的name.
}
}
class Demo8.4{
public static void main(String[] args){
Animal dog = new Animal("狗","白色");
Animal cat = new Animal("猫","黑色");
cat.eat();//猫在吃
}
}
5、this关键字调用其他的构造函数要注意的事项
① this关键字调用其他的构造函数时,this关键字必须要位于构造函数中的第一个语句。
② this关键字在构造函数中不能出现相互调用的情况,因为是一个死循环。
class Student{
int id; //学号
String name; //名字
//目前情况:存在同名的成员变量与局部变量,在方法内部默认是使用局部变量的
public Student(int id,String name){ //一个函数的形式参数也是属于局部变量
this(name); //调用了本类的一个参数的构造方法
//this(); //调用了本类无参的构造方法。
this.id = id; //局部变量的id给成员变量的id赋值
System.out.println("两个参数的构造方法被调用了...");
}
public Student(){
System.out.println("无参的构造方法被调用了...");
}
public Student(String name){
this.name = name;
System.out.println("一个参数的构造方法被调用了...");
}
}
class Demo8.5{
public static void main(String[] args){
Student s1 = new Student(110,"张三");
System.out.println("编号:"+ s1.id +" 名字:" + s1.name);
Student s2 = new Student("李四");
System.out.println("名字:" + s2.name);
}
}
6、需求: 使用java定义一个人类,人具备 id、name 、 age三个属性, 还具备一个比较年龄的方法。
要求: 必须 要写上构造函数,构造函数也必须要使用上this关键字。
class Person{
int id; //编号
String name; //姓名
int age; //年龄
//构造函数
public Person(int id,String name ,int age){
this.id = id;
this.name = name;
this.age = age;
}
//比较年龄的方法
public void compareAge(Person p2){
if(this.age>p2.age){
System.out.println(this.name+"大!");
}else if(this.age<p2.age){
System.out.println(p2.name+"大!");
}else{
System.out.println("同龄");
}
}
}
class Demo8.6{
public static void main(String[] args){
Person p1 = new Person(110,"张三",17);
Person p2 = new Person(119,"李四",9);
p1.compareAge(p2);
}
}
四、static关键字
1、目前存在的问题: 所有的学生都是中国的,有n个学生就会有n份中国的 数据存内存中,这样子会浪费内存。
目前方案: 把“中国”这个数据移动到数据共享区中,共享这个数据给所有的Student对象使用即可。
问题2: 如何才能把这个数据移动到数据共享区中共享呢?
解决方案: 只需要使用static修饰该数据即可。
2、静态的成员变量只会在数据共享区中维护一份,而非静态成员变量的数据会在每个对象中都维护一份的。
class Student{
String name;
//使用了static修饰country,那么这时候country就是一个共享的数据。
static String country = "中国";
//构造函数
public Student(String name){
this.name = name;
}
}
class Demo8.7{
public static void main(String[] args){
Student s1 = new Student("张三");
Student s2 = new Student("陈七");
s1.country = "其他";
System.out.println("姓名:"+s1.name+" 国籍:"+ s1.country);//其他
System.out.println("姓名:"+s2.name+" 国籍:"+ s2.country);//其他
}
}
3、 static可以修饰成员变量 :如果有数据需要被共享给所有对象使用时,那么就可以使用static修饰。
4、静态成员变量的访问方式
方式1: 可以使用对象进行访问。格式: 对象.变量名
方式2: 可以使用类名进行访问。格式: 类名.变量名
5、注意
① 非静态的成员变量只能使用对象进行访问,不能使用类名进行访问。
② 千万不要为了方便访问数据而使用static修饰成员变量,只有成员变量的数据是真正需要被共享的时候才使用static修饰。
③ static修饰成员变量的应用场景: 如果一个数据需要被所有对象共享使用的时候,这时候即可好实用static修饰。
6、static可以修饰成员函数
class Student{
String name; //非静态成员变量
static String country = "中国"; //静态的成员变量
public Student(String name){
this.name = name;
}
}
class Demo8.8{
public static void main(String[] args){
System.out.println("国籍:"+ Student.country);
}
}
7、需求: 统计一个类被使用了多少次创建对象,该类对外显示被创建的次数。
class Emp{
static int count = 0;//计数器
String name;
//构造代码块
{
count++;
}
public Emp(String name){
this.name = name;
}
public Emp(){ //每创建一个对象的时候都会执行这里的代码
}
public void showCount(){
System.out.println("创建了"+ count+"个对象");
}
}
class Demo8.9{
public static void main(String[] args){
Emp e1 = new Emp();
Emp e2 = new Emp();
Emp e3 = new Emp();
e3.showCount();
}
}