JavaSE:类与对象(2)

1.对象的构造以及初始化:

1.1初始化对象:

前面知识里,在java内部方法中定义一个局部变量(定义在方法、构造方法、代码块或者方法参数中的变量),必须要初始化,要不然会编译失败。

public static void main(String[]args){
    int a;
    System.out.println(a);
}

上面这个代码,要将a初始化之后,才能编译通过。

class Date{
    int year;
    int month;
    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(this.year+"年"+this.month+"月"+this.day+"日");
 }
}
public static void main(String[]args){
   Date d=new Date();
  d.printDate();
  d.setDate(2021,6,9);
  d.printDate();
}

 上面这个代码里:

1.每次对象创建好后调用 SetDate 方法设置具体日期,比较麻烦,有没有什么简便方法呢?
2.局部变量必须要初始化才能使用,为什么字段/成员变量声明之后没有赋值依然可以使用?

 1.2构造方法:

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

构造方法:

名字与类名相同。没有返回值类型,设置为void也不行。

一般情况下使用public修饰。

在创建对象的时候编译器自动调用,并且在对象的生命周期中只调用一次。

 


class Date{
    int year;
    int month;
    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(this.year+"年"+this.month+"月"+this.day+"日");
 }
}
public static void main(String[]args){
   Date d=new Date(2021,6,9);//这里创建了一个Date的对象,并没有显式调用构造方法,但是调用了Date(int.int,int)的构造方法
  d.printDate();
}

注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。

1.3构造方法的特性:

1.名字必须与类名相同。

2.没有返回值类型,设置为void也不行

3.由编译器自动调用,并且在对象的生命周期中只调用一次。

4.构造方法也可以重载(用户根据自己的需求,提供不同参数的构造方法)

class Date{
    int year;
    int month;
    int day;
//构造方法1
    public Date(){
    this.year=2001;
    this.month=12;
    this.day=29;
}
//构造方法2
    public Date(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+"日");
 }

上面代码中,Date类中,有两个名字相同,参数列表不同的构造方法,因此构成了构造方法的重载。可以在实例化对象的时候调用合适的构造方法。

如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。

 注意:一旦用户定义,编译器便不再生成默认的构造方法。

class Date{
    int year;
    int month;
    int day;
//默认的无参的构造方法
    public Date(){
    
 }
public void printDate(){
   System.out.println(this.year+"年"+this.month+"月"+this.day+"日");
 }
}
public static void main(String[]args){
   Date d=new Date();//这里创建了一个Date的对象,并没有显式调用构造方法,但是调用了Date(int.int,int)的构造方法
  d.printDate();
}

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

class Date{
    int year;
    int month;
    int day;
//构造方法1,这里可以通过this来调用构造方法2
    public Date(){
    this(2019,2,3);//记住这里this调用必须在第一句
    
}
//构造方法2
    public Date(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+"日");
 }

注意:

1.this(......)调用必须在第一句。

2.而且不能成环(比如上面这个代码,在构造方法1中用this调用了构造方法2,所以就不能在构造方法2中用this调用构造方法1)

构造方法大部分用public来修饰,特殊场景用private修饰(后面讲)

 1.4默认初始化:

上面提出了一个问题,为什么局部变量必须要赋初值,不然编译出错,而成员变量不用赋初值,就能通过编译。

要搞清楚这个过程,就要知道new背后发生的一切。

Date d=new Date;

在程序层面,就一句简单的代码,但在JVM层面要做许多事情

1.检测对应对象的类是否加载了,如果没有加载则加载(一个类只加载一次)

2.为对象分配空间。

3.处理并发安全问题。比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突。

初始化所分配的空间:

即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
数据类型                        默认值
byte                                0
char                                '\u0000'
short                               0
int                                   0
long                                0
boolean                          false      
float                                0.0f
double                            0.0
reference                        null

1.5就地初始化:

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) {
    }
}

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

2.封装:

面向对象程序的三大特征:封装,继承,多态。在类与对象阶段,主要研究就是封装特性,何为封装,就是套壳屏蔽细节

举一个例子:对于计算机这个复杂设备,计算机厂商在出厂时,在外部套上壳 子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可

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

封装扩展之包:

java中允许使用包将类组织在一个集合中,借助包可以方便地组织自己的代码,并将自己的代码与别人提供的代码库分开管理,在面向对象体系中,提出了软件包的概念,为了更好管理类,就把多个类收集在一起成为一组,称为软件包。比如为了更好管理歌曲,就把属性相似的歌曲放在同一个文件下面,也可以在某个文件下进行详细的分类 

自定义包:

使用包的主要原因是确保类名唯一性,假如有两个程序员不约而同的建立了Employee类,只要将这些类放置在不同的包中,就不会产生冲突,为了保证包名的唯一性,要用一个因特网域名(这显然是唯一的)以逆序的方式作为包名,然后对于不同的工程使用不同的子包。比如:域名horstmann.com。如果逆序来写,就得到包名com.horstmann。然后追加一个工程名,如com.horstmann.corejava。如果再把Employee类放在这个包里面,那么这个类的”完全限定“名就是com.horstmann.corejava.Employee。

注释:从编译器来看,嵌套包之间没有任何关系,例如,java.until包和java.until.jar包毫无关系,每一个包都是独立的类集合。

类的导入:

一个类可以使用所属包的所有类,以及其他包中的公共类

我们可以采用两种方式访问另一个包中的公共类。

第一种就是使用完全限定名字,就是包名后面跟着类名,例如:

java.time.LocalDate.today=new java.time.LocalDate();

这是很繁琐的方法,第二种方法就是使用import语句。import语句是一种引用包中的各个类的简捷方式,一旦使用了import语句,在使用类的时候,就不必写出类的全名了。可以使用import语句导入一个特定的类或者整个包。import语句应该位于源文件顶部(但位于package语句后面)。例如:用下面这条语句导入java.util包中的所有类(注意在import导包的时候,要具体到某个类上)

import.java.time.*;

然后,就可以使用此包中所有的类成员。比如:LocatDate today =new LocalDate();就不用在前面加上包的前缀。还可以导入一个包的特定类:

import java.time.LocalDate;

 java.time.*的语法比较简单,对代码规模也没有任何负面影响,不过,如果能够明确的指出所导入的类,代码会让读者更加准确的知道你使用了那些类。大多情况,可以只导入你需要的包,但在发生文件名冲突的时候,就要注意包了,java.util和java.sql包都有Date类,如果在同一个文件中导入这两个包:

import.java.util.*;

import.java.sql.*;

 此时在使用Date类的时候,就会出现一个编译错误:
Date.today;//ERROR--java.util.Date or java.sql.Date?        

此时编译器不知道你到底要用哪一个包下面的Date类。如果这两个Date类都要使用,又该怎么办呢?答案:在每个类名前面加上完整的包名。

静态导入包:

有一种import语句允许导入静态方法和静态字段,而不是只有类。例如:
import static java.lang.System.*;

然后就可以使用System类的静态方法和静态字段,且不用加类名的前缀:比如println来打印一个Hello world,就可以写成,out.println("Hello world");省略掉了System前缀。

3.static成员:

1.static修饰成员变量:

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

静态成员变量的属性:

1.不属于具体某个对象,是类的属性,所有对象所共享的,不储存在某个对象的空间中。

2.既可以通过对象访问,也可以通过类名访问,一般更推荐用类名访问。

3.类变量(static修饰的成员变量)储存在方法区当中。

4.生命周期伴随类的一生(随类的加载而创建,随类的卸载而销毁)

 class Main {
   public String name;
   public int age;
   public static int a;
   public Main(String name,int age){

   }

    public static void main(String[] args) {
        Main main1=new Main("aaa",20);
        Main main2=new Main("bbb",21);
        main1.a=2;
        main2.a=3;
    }
}

调试上面这个代码,然后在监视窗口可以看到,静态成员变量a并没有储存到某个具体的对象中。

注意:静态成员变量不依赖于对象

2.static修饰的成员方法:

被static修饰的成员方法,也被称为类方法,和成员变量一样不依赖于对象,不是某个对象所特有的,静态成员变量一般是通过静态成员方法来访问的。

看上面这个代码,在func(静态方法)中调用了name(非静态成员变量)在静态方法中是不能出现this关键字的,因为静态成员方法是不依赖于对象的,而this是指向当前对象的引用,依赖于对象的,在一个静态方法中调用非静态的变量,是行不通的。所以只能通过在静态方法中实例化对象,通过对象来调用非静态成员变量。

看上面这个代码,可以在静态方法中实例化一个对象,然后通过对象调用非静态的成员变量(这种也是可行的)

在静态方法中调用非静态方法也是一样。因为非静态方法有this参数,在静态方法调用时无法传递this引用,但是可以通过实例化对象来进行调用非静态方法(如右图)      

总结:

调用静态成员变量或者静态方法:1.通过对象的引用调用。2.通过类名来调用

在静态方法中,不能直接使用非静态的成员变量和成员方法,要想使用,要通过new对象,然后通过对象引用访问。

但是在非静态方法中,可以直接使用静态成员变量和静态成员方法

注意:静态的不依赖于对象,属于类,不属于对象。

3.static成员变量的初始化:

注意:静态方法一般不在构造方法中进行初始化,构造方法初始化的是与对象相关的实例属性

静态成员变量初始化分以下几种:

(1)就地初始化

(2)通过get set方法初始化

(3)构造方法【较少使用】

(4)代码块初始化:静态代码块、非静态代码块/实例代码块/构造代码块、

同步代码块(暂时不提)

1.就地初始化:

如下图:

在定义变量的时候就进行赋值,称就地初始化。

2. 通过get set方法初始化:

如下图:

3.代码块初始化:

那啥叫代码块呢?代码块是一段被{ }括起来的一段代码,代码块可以在类,方法中使用。

一般用来成员的初始化。

  1. 实例方法块是在对象的每个方法执行之前执行的代码块,它使用花括号括起来,并且没有任何修饰符,不带参数。实例方法块可以用于在每个实例方法执行之前执行一些通用的操作,例如检查对象状态或设置对象属性。

    如果有多个实例代码块:则会按照定义顺序执行。

  2. 静态代码块(也称为静态初始化块):使用关键字 static 定义的代码块。静态代码块在类加载时执行,并且只执行一次。用来初始化静态成员变量

在静态代码块中也不能用来初始化非静态成员变量。因为静态代码块不依赖于对象,但是成员变量是会有this调用,其是依赖对象的,所以不能初始化。

静态代码块不管创建多少次对象,其只会执行一次:

运行结果:

可以得出:不管生成多少个对象,静态代码块只会执行一次。

如果出静态代码块,都是静态的情况下,输出的结果和定义静态代码块的顺序有关。

运行结果:

那如果在一个类中,同时存在静态代码块,实例代码块和构造方法,那执行顺序又是怎么样呢?

请看下面这个代码:

运行结果:

打乱顺序的运行结果也是如此,可以自行操作。

可知,静态代码块先执行,其次是实例代码块执行,最后是构造方法被执行。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值