面向对象的初步认知

1.什么是面向对象

Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。

2.面向对象与面向过程

以面向过程方式来进行处理,就关注洗衣服的过程,洗衣机是怎么来洗衣服,如何来甩干的等细节。以面向对象方式来进行处理,就不关注洗衣服的过程,只需要将衣服放进洗衣机,倒入洗衣粉,启动开关即可,通过对象之间的交互来完成的。

二、类定义和使用

面相对象程序设计关注的是对象,而对象是现实生活中的实体,比如:洗衣机。但是洗衣机计算机并不认识,需要开发人员告诉给计算机什么是洗衣机。
a4245393e8d242568cc8a31b563c3329.png

上图左侧就是对洗衣机简单的描述,该过程称为对洗衣机对象(实体)进行抽象(对一个复杂事物的重新认知),但是这些简化的抽象结果计算机也不能识别,开发人员可以采用某种面相对象的编程言来进行描述,比如:Java语言。

1.简单认识类

类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了。
比如:洗衣机,它是一个品牌,在Java中可以将其看成是一个类别。
属性:产品品牌,型号,产品重量,外观尺寸,颜色...
功能:洗衣,烘干、定时.... 

2.类的定义格式

在java中定义类时需要用到class关键字,具体语法如下


// 创建类
class ClassName{
field; // 字段(属性) 或者 成员变量
method; // 行为 或者 成员方法
}

class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
接下来为洗衣机定义一个类
class WashMachine{
public String brand; // 品牌
public String type; // 型号
public double weight; // 重量
public double length; // 长
public double width; // 宽
public double height; // 高
public String color; // 颜色
public void washClothes(){ // 洗衣服
System.out.println("洗衣功能");
}
public void dryClothes(){ // 脱水
System.out.println("脱水功能");
}
public void setTime(){ // 定时
System.out.println("定时功能");
}
}
注意事项
1.类名注意采用大驼峰定义
2.成员前写法统一为public,后面会详细解释
3.此处写的方法不带 static 关键字. 后面会详细解释
4. 一般一个文件当中只定义一个类
5. main方法所在的类一般要使用public修饰(Eclipse默认会在public修饰的类中找main方法)
6. public修饰的类必须要和文件名相同

3.类的实例化

3.1什么是实例化
在上一章中已经讲了new的作用(这里不详细讲了,本节重点讲构造方法)比如给上面洗衣机类实例化:

WashMachine a =new WashMachine();     这个语句创建了一个名为a的自定义(这里为洗衣机类)类的类型,我们称为对象(java里面一切都是对象,比如数组类型,整型类型,浮点型类型等),实例化过程中会给他分配内存空间以及初始化(构造方法的作用)。实例化后就可以访问类里面的成员变量和成员方法了(即自定义类里面的变量和方法)

下面定义一个狗类并实例化

class PetDog {
public String name;//名字
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
public void wag() {
System.out.println(name + ": 摇尾巴~~~");
}
}
public class Test1{
public static void main(String[] args) {
PetDog dogh = new PetDog(); //通过new实例化dogh这个对象
dogh.name = "阿黄";
dogh.barks();
dogh.wag();
PetDog dogs = new PetDog();//实例化dogs这个对象
dogs.name = "赛虎";
dogs.barks();
dogs.wag();
}
}
输出结果:
阿黄: 旺旺旺~~~
阿黄: 摇尾巴~~~
赛虎: 旺旺旺~~~
赛虎: 摇尾巴~~~
注意事项
1.new 关键字用于创建一个对象的实例.
2.使用 . 来访问对象中的属性和方法.
3.同一个类可以创建多个实例.

4.类和对象的说明

 1. 类只是一个模型一样的东西,用来对一个实体(对象)进行描述,限定了类有哪些成员.
2. 类是一种自定义的类型,可以用来定义变量(类里面定义的变量都不会分配内存,只有实例化才分配空间)
 3. 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量  

三、this关键字

1.为什么要有this关键字

先看一个日期的类

//第1行public class Date {
//2行public int year;
//3行public int month;
//4行public int day;
public void setDay(int y, int m, int d){
//5year = y;
//6month = m;
//7day = d;
}
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 
Date d1 = new Date();
Date d2 = new Date();
// 对d1,d2的日期设置
//9行 d1.setDay(2020,9,15);
//10行 d2.setDay(2023,11,16);
不知道你是否可以发现上述代码潜在的问题,我们思考一个问题:上面代码第2行的year和第5行的year是同一个吗,答案肯定不是的,问题就出在这里,2、3、4行里面的变量是属于类的,不属于对象(当你在2、3、4行就赋值时,你这个类中所有对象的变量初始值都一样,不推荐这样写),当我们在9、10行调用成员方法时,第一次调用时d1对象(year=2020,month=9,day=15)...第二次调用时d2(year=2023,month=11,year=16)那是不是d1里面的值也发生了改变呢,其实没有改变,正是因为this关键字。
再看一个代码 形参名不小心与成员变量名相同
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量给参数?估计自己都搞不清楚了。此时就要用到this关键字。

2.什么是this

this表示当前对象(成员方法运行时调用该成员方法的对象,在代码中直接把this看成对象名即可),在成员方法中所有成员变量的操作,都是通过该this去访问。只不过所有操作都是编译器自动完成。
现在改正刚才的代码
//第1行public class Date {
//2行public int year;
//3行public int month;
//4行public int day;
public void setDay(int y, int m, int d){
this.year = y;            //当d1开始调用时,此代码看成d1.year=y;d2调用时看成d2.year=y;
this.month = m;        //..............................看成d1.month=m;.....................同上
this.day = d;              //....................同上................
}
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 
Date d1 = new Date();
Date d2 = new Date();
// 对d1,d2的日期设置
//9行 d1.setDay(2020,9,15);
//10行 d2.setDay(2023,11,16);
将this换为对象后,就可以解释刚才的疑问了(一个year,一个d1.year,一个d2.year,他们能一样吗.....)
注意:其实这个代码this 可以省略,因为编译过程中会自动生成this...但是,建议以后把this 都加上,因为下面这个代码不加this 会出问题。
public void setDay(int year, int month, int day){
year = year;       //如果不加this,编译器会认为这两个year都是局部变量(因为你传了一个year过来,将局部变量赋值给自己,编译器会报一个警告)
month = month;
day = day;
}

3.this的特征

1. this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
2. this只能在"成员方法"中使用
3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象

四、对象的构造及初始化

1.如何初始化对象

通过前面知识点的学习知道,在Java方法内部定义局部变量时,必须要初始化,否则会编译失败
public static void main(String[] args) {
int a;
System.out.println(a);
}
// Error:(26, 28) java: 可能尚未初始化变量a
但是自定义类中成员变量没有初始化也可以正常打印,在数组那里讲了因为new的作用默认值为0
看下面代码
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);
}
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();               //因为new作用,默认初始化为0,结果为0/0/0
d.setDate(2021,6,9);   //给变量赋值
d.printDate(); //赋值后结果为   2021/6/9
}
//屏幕结果
//0/0/0
//2021/6/9
需要调用之前写的SetDate方法才可以将具体的日期设置到对象中。 通过上述例子发现两个问题:
1. 每次对象创建好后调用SetDate方法设置具体日期,比较麻烦,那对象该如何初始化?(利用构造方法进行初始化或者就地初始化,下面会讲到)
2. 局部变量必须要初始化才能使用,为什么变量声明之后没有给值依然可以使用?(new作用)

2.构造方法

2.1什么是构造方法
构造方法(也称为构造器)是一个特殊的成员方法, 名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内 只调用一次,该方法可以有多个
public class Date {
public int year;
public int month;
public int day;
// 构造方法:
// 名字与类名相同,没有返回值类型,设置为void也不行
// 一般情况下使用public修饰
// 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
System.out.println("Date(int,int,int)方法被调用了");
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
// 此处创建了一个Date类型的对象,并没有显式调用构造方法
Date d = new Date(2021,6,9); // 输出Date(int,int,int)方法被调用了
d.printDate(); // 2021-6-9
}
}
注意:
1.构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
2.名字与类名相同,没有返回值类型,设置为void也不行
3.一般情况下使用public修饰
4.构造方法可以有多个(方法重载)
2.2什么时候调用构造方法
使用 new关键字创建对象时由编译器自动调用(如果有多个,根据参数判断调用哪个)并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
如果没有自己定义构造方法,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的,并且他的方法体是空的。 在Java中, 默认初始化是在对象构造过程中自动完成的 。当一个对象被创建时,如果它的成员变量没有被显式地初始化,Java虚拟机(JVM)会自动为这些变量提供默认值。
通过以上内容,我们可以总结出New的作用,
1.创建内存空间。且默认初始化为0
2.如果没有定义构造方法,编译器会生成一份默认的构造方法,
3.调用构造方法。
注意:如果自己定义了构造方法,new中参数类型与个数要匹配,见以下代码。
public class Date {
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
public static void main(String[] args) {
Date d = new Date();                // 但实际情况是:编译期报错,参数不匹配。
Date d = new Date(1,2,3);//这个代码才是正确的。
}
注意:构造方法中,可以通过this调用其他构造方法来简化代码,见以下代码
public class Date {
public int year;
public int month;
public int day;
// this(1900,1,1);必须是构造方法中第一条语句
public Date(){
//System.out.println(year);如果注释不取消掉,编译会失败
this(1900, 1, 1);
}
// 带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
注意: this(...)必须是构造方法中第一条语句
this 调用其他构造方法过程中不能形成环,见以下代码
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成闭环(递归调用)
编译报错:Error:(19, 12) java: 递归构造器调用
*/
7. 构造方法绝大多数情况下使用public来修饰,特殊场景下会被private修饰(后序讲单例模式时会遇到)

3.初始化

3.1利用构造方法进行初始化

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;
}
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);
d.printDate(); 
}
}
// 屏幕输出方法2021-6-9。

3.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();
}
}

五、封装

面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。

1.封装的概念

从语法上来说,就是被private修饰的成员变量或者成员方法,只能在当前类中使用

2.访问限定符

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

78c562e967094f2b8bd04d3b8ed9bcf5.png

说明
protected主要是用在继承中,继承部分详细介绍子类与非子类。
default权限指:什么都不写时的默认权限

访问权限除了可以限定类中成员的可见性,也可以控制类的可见性(自定义类不能私密和保护)如果把构造方法私密了,在类外便不能实例化对象了)

如果一个成员变量被private修饰,我们不能直接在类外访问,见以下代码

public class Computer {
private String cpu;
public Computer(String cpu) {
this.cpu = cpu;
}
}
public class TestComputer {
public static void main(String[] args) {
Computer p = new Computer("i7");
 System.out.println(p.cpu);  // private属性:只能在Computer类中访问,这里编译器报错。
}
}
那我们如何 在类外访问被private修饰的变量呢
1.我们可以使用公共的方法间接访问(也可以使用构造方法,原理是一样的)

class MyClass {

// 私有变量

private int s;

// 构造函数

public MyClass(int value) { s = value;

}

// 普通公共方法

public void pin() {

// 在普通方法内部访问私有变量

System.out.println("私有变量的值是: " + s);

}

}

public class TestStudent { public static void main(String[] args) {

// 创建MyClass的实例

MyClass a = new MyClass(10);

// 调用MyClass实例的普通方法 a.pin();

}

}

//屏幕结果:  私有变量的值是: 10

2. 使用封装方法
封装方法分为get方法(读取私密变量的值)和set方法(设置或修改私密变量的值)
先看以下代码

package Testdmol2;

public class U {

private int age=10;

public int getAge() {           //封装方法
return age;  
 }

public void setAge(int age) {      //封装方法
this.age = age;  
}
}

package Testdmo1;
import Testdmol2.U;

public class J3_12two {

public static void main(String[] args) {
U u=new U();

System.out.println(u.getAge());
}

}

一个变量代表一个封装方法(因为封装方法(get方法)只能有一个返回值),我们可以通过下面的技巧快速写封装方法
我们在idea(建议使用Idea,不要用eclipse)中选中任意一行,点击鼠标右键,选中generate(生成),再点击getter和setter,再选中要修改的私密变量的值,就会自动生成以下代码

public int getAge() {

return age;  
 }

public void setAge(int age) {
this.age = age;  
}

总结:想访问私密变量,找公共方法即可,编译器提供了封装方法(只是公共方法的一种,变方便了,不需要自己写了。)

例题:尝试在不同包中访问一个默认类型的成员变量(使用封装方法)

package Testdmo1;

import Testdmol2.U;

public class J3_12two { public static void main(String[] args) {

U u=new U();

System.out.println(u.getAge());

}

}

package Testdmol2;
public class U {
int age=10;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

//输出结果为10

3.封装扩展之包

3.1包的概念

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。 在同一个工程中允许存在相同名称的类(参数类型也相同),只要处在不同的包中即可一个Java文件只能有一个包。一般一个类一个java文件。但是一个文件可以有多个类。

3.2导入包中的类

Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
但是这种写法比较麻烦一些, 可以 使用 import 语句导入包 .
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
不知道你看了上面的代码有没有疑惑,我们在数组那里引用Arrays类时,并没有实例化,而是直接通过Arrays.方法名 来调用,这是因为这个方法是静态方法,这些方法可以直接通过类名调用,不需要实例化Arrays类。这些静态方法都是类方法,它们只与类本身相关,与对象无关。静态方法的特点是,它们可以在不创建对象的情况下直接通过类名调用。
如果需要使用 java.util 中的其他类, 可以使用 import java.util.*
import java.util.*;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
注意:多个包类名不能重复
import java.util.Date;
import java.sql.Date; 
 //编译器会报错
import java.util.*中*号可以重复:在这种情况下需要使用完整的类名
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
Date date = new Date();// util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
java.util.Date date = new java.util.Date();//这样才正确。
System.out.println(date.getTime());
}
}
// 编译出错
Error:(5, 9) java: 对Date的引用不明确
java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配

3.3自定义包

基本规则
1.在文件的最上方加上一个 package 语句指定该代码在哪个包中.
2.包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
3.包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
4.如果一个类没有 package 语句, 则该类被放到一个默认包中(即src).
此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了.
1abed64038164c5386bb45709329046e.png

3.4包的访问权限控制举例

package com.bit.demo1;
public class Computer {
private String cpu; // cpu
private String memory; // 内存
public String screen; // 屏幕
String brand; // 品牌
public Computer(String brand, String cpu, String memory, String screen) {
this.brand = brand;
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
public void Boot(){
System.out.println("开机~~~");
}
public void PowerOff(){
System.out.println("关机~~~");
}
public void SurfInternet(){
System.out.println("上网~~~");
}
package com.bit.demo2;
import com.bit.demo1.Computer;
public class TestComputer {
public static void main(String[] args) {
Computer p = new Computer("HW", "i7", "8G", "13*14");
System.out.println(p.screen);
// System.out.println(p.cpu); // 报错:cup是私有的,不允许被其他类访问
// System.out.println(p.brand); // 报错:brand是default,不允许被其他包中的类访问
}
}        //关于这些修饰符后面有更详细的解释(继承里面)
// 注意:如果去掉Computer类之前的public修饰符,代码也会编译失败(默认类型只能在同一个包访问)
上面两个代码中Computer类位于com.bit.demo1包中,TestComputer位置com.bit.demo2包中:在第二个文件里引用第一个文件的包,那么就可以访问他的类了。
注意:一般情况下成员变量设置为private,成员方法设置为public. 如果构造方法设置为私密,则不能实例化对象

六、static关键字

1.为什么要有static

1.对变量:如果想要使所有对象有相同的初始值,就要使用static.
例如三个对象s1、s2、s3,每个对象都有自己特有的名字、性别,年龄,学分绩点等成
员信息,这些信息就是对不同学生来进行描述的,但是如果他们班级一样,此时可以用static'
2.对方法:又例如想改变静态变量的值或给静态变量赋值可以用静态方法
3.对调用方式:你可以直接通过类名来调用静态成员方法与变量而不需要创建类的实例,变方便了。

2.static修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 静态变量存储在方法区当中(后面介绍)
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
下面代码演示了静态变量既可以通过对象访问,也可以通过类名访问
public class Student{
public String name;
public String gender;
public int age;
public double score;
public static String classRoom = "Bit306";
public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
System.out.println(Student.classRoom);
// 也可以通过对象访问:但是classRoom是三个对象共享的,这种访问比较麻烦
Student s1 = new Student("Li leilei", "男", 18, 3.8);
System.out.println(s1.classRoom);
}
}

3.static修饰成员方法

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

共享访问:由于所有对象共享同一个静态成员方法的副本,因此任何一个对象对静态成员方法的修改都会影响到其他所有对象。这也是为什么静态成员方法通常用于执行不依赖于单个对象状态的操作。

我们在封装那里介绍了类外访问私密变量的方法,那在类外如何访问私密静态变量呢

但是想在类外访问私密静态变量, 一般使用静态方法,(也可以使用普通公共方法),见以下代码。

//使用静态方法

class Student{

private static String classRoom = "Bit306";

public static String getClassRoom(){  //静态方法

return classRoom;

}

}

public class TestStudent { public static void main(String[] args) {

System.out.println(Student.getClassRoom());

}

}

//屏幕输出 Bit306

//使用普通方法:因为在普通成员方法里可以访问静态方法及静态变量(因为静态是对象共享的)

 class MyClass {

// 静态私有变量

private static int s;

// 构造函数

public MyClass(int value) {

s = value;

}

// 普通方法

public void pin() {

// 在普通方法内部访问静态私有变量

System.out.println("静态私有变量的值是: " + s);

}

}

public class TestStudent {

public static void main(String[] args) {

// 创建MyClass的实例

MyClass a = new MyClass(10);

// 调用MyClass实例的普通方法

a.pin();

}

}

//输出: 静态私有变量的值是: 10

以上两种方法都要我们自己写,其实我们也可以使用静态的封装方法,与普通的封装方法一样,在选择变量时选静态的就可以了,见以下代码


package Testdmol1;

import Testdmol2.U;

public class J3_12two {

public static void main(String[] args) {

U u=new U();

System.out.println(u.getClassRoom());

}

}

package Testdmol2;
public class U {

int age=10;

private static String classRoom = "Bit306";

//下面红色代码为使用上面说的技巧后编译器自动生成的 选变量时选中classroom

public static String getClassRoom() {  

return classRoom;

}

 public static void setClassRoom(String classRoom) {  

U.classRoom = classRoom;

}

//屏幕输出结果:Bit306

静态方法特性
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能在静态方法中访问任何非静态成员变量(因为静态方法不属于对象,同时不能在静态方法引用this)
public static String getClassRoom(){
System.out.println(this);
return classRoom;
}
// 编译失败:Error:(35, 28) java: 无法从静态上下文中引用非静态 变量 this
public static String getClassRoom(){
age += 1;
return classRoom;
}
// 编译失败:Error:(35, 9) java: 无法从静态上下文中引用非静态 变量 age

4. 静态方法中不能调用任何非静态方法,
public static String getClassRoom(){
doClass();
return classRoom;
}
// 编译报错:Error:(35, 9) java: 无法从静态上下文中引用非静态 方法 doClass(
注意:在普通成员方法里可以访问静态方法及静态变量(因为静态是对象共享的,)

4.static成员变量初始化

注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性(即普通成员变量)
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化

4.1就地初始化

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

public class Student{
private String name="huihuang";
private int age=81;
private static String classRoom = "计科3班";
// ...
}

4.2静态代码块初始化

那什么是代码块呢?继续往后看 ~~~

七、代码块

1.代码块概念以及分类

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块
构造块
静态块
同步代码块(后续讲解多线程部分再谈)

1.1普通代码块

普通代码块:定义在方法中的代码块
public class Main{
public static void main(String[] args) {
{ //直接使用{}定义,普通方法块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}

// 执行结果
x1 = 10
x2 = 100
这种用法较少见

1.2 构造代码块

构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块构造代码块一般用于初始化实例成员变量(即非静态成员变量)

public class Student{
//实例成员变量
private String name;
private String gender;
private int age;
private double score;
public Student() {
System.out.println("I am Student init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
public void show(){
System.out.println("name: "+name+" age: "+age+" sex: "+sex);
}
}
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.show();
}
}
// 运行结果
I am instance init()!
I am Student ()!
name: bit age: 12 sex: man
从以上的结果可以看出代码块的内容比构造方法先执行, 现在再补充一下new的作用
在调用构造方法前会先执行代码块的内容。

1.3 静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
//实例代码块
{
this.name = "bit";
this.age = 12;
this.gender = "man";
System.out.println("I am instance init()!");
}
// 静态代码块
static {
classRoom = "bit306";
System.out.println("I am static init()!");
}
public Student(){
System.out.println("I am Student init()!");
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
}
}
/*运行结果
I am static init()!
I am instance init()!
I am Student init()!
I am instance init()!         //发现这里静态内容没打印
I am Student init()!
*/
从以上代码的结果可以看出静态代码块先执行,再执行实例代码块,最后调用构造方法,同时发现静态代码块只执行一次
注意事项
静态代码块不管生成多少个对象,其只会执行一次(因为静态成员变量是类的属性,是在JVM加载类时开辟空间并初始化的),而实例代码块只有在实例化对象时才会执行。
如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行

八、对象的打印

如果直接给对象名打印,结果是一个引用(地址)

public class Person {

String name;

String gender;

int age;

public Person(String name, String gender, int age) {

this.name = name;

this.gender = gender;

this.age = age;

}

public static void main(String[] args) {

Person person = new Person("Jim", "男", 18);

System.out.println(person);

}

}

//输出结果 Person@4554617c

上面也介绍了打印对象的方法,我们回忆一下一开始的狗类

class PetDog {
public String name;//名字
public String color;//颜色
// 狗的属性
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(name + ": 摇尾巴~~~"); //在类里面直接写name即可打印值
                                                                //在类外面写对象名.name即可打印值
}
public class Test1{
public static void main(String[] args) {
PetDog dogh = new PetDog(); //通过new实例化dogh这个对象
dogh.name = "阿黄";
dogh.barks();
dogh.wag();
PetDog dogs = new PetDog();//实例化dogs这个对象
dogs.name = "赛虎";
dogs.barks();
dogs.wag();
}
}
输出结果:
阿黄: 旺旺旺~~~
阿黄: 摇尾巴~~~
赛虎: 旺旺旺~~~
赛虎: 摇尾巴~~~
以上方法太过复杂,下面介绍一个简单的方法。
答案:使用toString方法即可(以后会介绍原理,现在只要知道用即可)
blic class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String toString() {
return "[" + name + "," + gender + "," + age + "]";
}
public static void main(String[] args) {
Person person = new Person("Jim","男", 18);
System.out.println(person);
}
}
// 输出结果:[Jim,男,18]
我们在idea(建议用idea,不要用eclipse)中选中任意一行,点击鼠标右键,选中generate(生成),再点击tostring,就会自动生成以下代码
public String toString() {
return "[" + name + "," + gender + "," + age + "]";
}
有了这个代码后就可以直接给对象名打印,结果就不是地址了
补充一个小技巧:我们在idea(建议用idea,不要用eclipse)中选中任意一行,点击鼠标右键,选中generate(生成)再点击constuctor(构造方法),再选想要赋值的变量,便会自动生成一个构造方法。快去试试吧,如果要选多个变量,记得按shift键。

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐