目录
一、对象的构造及初始化
1.1 如何初始化对象
public static void main(String[] args) {
int a;
System.out.println(a);
}
上述代码在方法内部使用了一个定义但未初始化的局部变量,编译会报错,在使用前要对其进行赋值。
public static void main(String[] args) { Date date=new Date(); date.printDay(); System.out.println("====="); date.Setday(2023,3,22); date.printDay(); }
上述代码实例化一个对象并利用函数Setday对其赋值,但在赋值前并未报错,这是因为在成员变量未赋初值时,编译器会给类中的成员变量依其类型给一个默认值。每次创建对象后都要调用Setday为对象赋值,那对象该如何初始化呢?
1.2 构造方法
1. 概念
构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
public class Date {
public int year;
public int month;
public int day;
public void printDay(){
System.out.println(this.year+"/"+this.month+"/"+this.day);
}
public Date(int year,int month,int day){
this.year=year;
this.month=month;
this.day=day;
}
public static void main(String[] args) {
Date date=new Date(2023,3,22);
date.printDay();
}
}
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
2. 特性
1. 名字必须与类名相同。
2. 没有返回值类型,设置为void也不行。
3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)。
4. 构造方法可以重载
public class Date { public int year; public int month; public int day; public void printDay(){ System.out.println(this.year+"/"+this.month+"/"+this.day); } //方法重载 //无参的构造方法(用于对象属性值都相同) public Date(){ this.year=2023; this.month=3; this.day=22; } //有参的构造方法(用于对象属性值不相同) public Date(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public static void main(String[] args) { Date date=new Date();///传不传参都可以 date.printDay(); }}
5.如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
注意:一旦用户定义构造方法,编译器则不再生成。
public class Date {
public int year;
public int month;
public int day;
public void printDay(){
System.out.println(this.year+"/"+this.month+"/"+this.day);
}
public static void main(String[] args) {
Date date=new Date() ;
date.printDay();
}
}
上述Date类中,没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法。
6. 构造方法中,可以通过this调用其他构造方法来简化代码。
public class Date { public int year; public int month; public int day; public void printDay(){ System.out.println(this.year+"/"+this.month+"/"+this.day); } public Date(int year,int month,int day){ this.year=year; this.month=month; this.day=day; } public Date(){ this(2023,3,22); } public static void main(String[] args) { Date date=new Date(); date.printDay(); }
注意:this(...)必须是构造方法中的第一条语句,不能形成环。
public Date(){ //环
this(2023,3,22);
}
public Date(int year, int month, int day) {
this();
}
7. 绝大多数情况下使用public来修饰,特殊场景下会被private修饰。
1.3 默认初始化
上述中局部变量使用时必须初始化,而成员变量则不用,这是为什么呢?
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; this.day=day; }public static void main(String[] args) { Date date=new Date(); date.printDay(); }}
要弄清楚这个问题,要了解new关键字的作用
Date date=new Date();
程序中只是一条语句,而在JVM层面,则做了很多事:检测对象对应的类是否加载了,如果没有加载则加载;为对象分配内存空间;处理并发安全问题;初始化所分配的空间;设置对象头信息;调用构造方法,给对象中各个成员赋值。
数据类型 | 默认值 |
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
1.4 就地初始化
在声明成员变量时就赋初值
public class Date {
public int year = 2023;
public int month = 3;
public int day = 22;
public Date(){
}
public Date(int year, int month, int day) {
}
public static void main(String[] args) {
Date d1 = new Date(2022,3,22);
Date d2 = new Date();
}
}
注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中。
二、封装
2.1 封装概念
面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性,封装简单来说就是套壳屏蔽细节。
例如:一台家用的电脑,有鼠标、显示器、键盘、主机,主机中有主板,提供给用户的就是:开关机、、通过键盘输入、显示器、usb接口等,让用户和计算机进行交互。
但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户
只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。
2.2 访问限定符
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中有四种限定符:private、default、protected和public。
public:可以理解为一个人的外貌特征,谁都可以看得到;default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了;private:只有自己知道,其他人都不知道。protected主要是用在继承中;default权限指:什么都不写时的默认权限;访问权限除了可以限定类中成员的可见性,也可以控制类的可见性。
public class Person { private String name;//只能在类Person中使用 private int age; public Person(){ System.out.println("不传参的构造方法"); } public Person(String name,int age){ this.name=name; this.age=age; System.out.println("传递两个参数的构造方法"); } public void Sleep(){ System.out.println(name+"正在睡觉"); } public void print(){ System.out.println("name:"+name+"age:"+age); } }
public class Text2 {//同文件(包)下的不同类 public static void main(String[] args) { Person person1=new Person("zhangsan",10); System.out.println("name:"+person1.name);//会报错 } }
public class Person { String name;//default属性,同文件(包)下都可使用 int age; public Person(){ System.out.println("不传参的构造方法"); } public Person(String name,int age){ this.name=name; this.age=age; System.out.println("传递两个参数的构造方法"); } public void Sleep(){ System.out.println(name+"正在睡觉"); }public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class Text2 {同文件(包)下的不同类 public static void main(String[] args) { Person person1=new Person("zhangsan",10); System.out.println(person1.getName());//可以通过调用函数获取对象属性值 System.out.println(person1.getAge()); } }
注意:一般情况下成员变量设置为private,成员方法设置为public
2.3 封装扩展之包
1.包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
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());
}}
如果需要使用 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.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
// util 和 sql 中都有 Date 类, 会出现编译错误
Date date = new Date();
System.out.println(date.getTime());
}}
在这种情况下就需要使用完整的类名。
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
System.out.println(date.getTime());
}}
可以使用import static导入包中静态的方法和字段。
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
int x = 30;
int y = 40;// int result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
int result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}}
3.自定义包
规则:在文件的最上方加上一个 package 语句指定该代码在哪个包中;包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式;包名要和代码路径相匹配.,例如创建com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码;如果一个类没有 package 语句, 则该类被放到一个默认包中。
创建包的步骤:
1.在 IDEA 中先新建一个包: src ---> New ---> Package
2.在弹出的对话框中输入包的名称。
3.在包中创建类, 包---> New---> class, 之后后输入类名。
4.打开文件位置就可以看到磁盘上目录结构已创建
5.在新创建的Text1.java文件最上方有一个package语句
4.常见的包
1. java.lang:系统常用基础类。
2. java.lang.reflect:java 反射编程包。
3. java.net:进行网络编程开发包。
4. java.sql:进行数据库开发的支持包。
5. java.util:是java提供的工具程序包。
6. java.io:I/O编程开发包。
三、static成员
3.1 再谈学生类
学生类实例化三个对象s1、s2、s3,每个对象都有自己特有的名字、性别,年龄,学分绩点等成员信息,这些信息是对不同学生进行描述的,例如
public class Student{
// ...
public static void main(String[] args) {
Student s1 = new Student("Li leilei", "男", 18, 3.8);
Student s2 = new Student("Han MeiMei", "女", 19, 4.0);
Student s3 = new Student("Jim", "男", 18, 2.6);
}
}
如果三个同学是一个班的,那么他们上课在同一个教室,那能否给类中再加一个成员变量,来保存同学上课时的教室呢?答案是不行的。
之前在Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量),因为需要使用这些信息来描述具体的学生。而现在要表示学生上课的教室,这个教室的属性并不需要每个学生对象中都存储一份,而是需要让所有的学生来共享。在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
3.2static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
静态成员变量的特性:不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中;既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问;类变量存储在方法区当中;生命周期伴随类的一生。
public class Student { public String name; public String gender; public int age; public double score; public static String classRoom="bit110"; public Student(String name,String gender,int age,double score){ this.name=name; this.gender=gender; this.age=age; this.score=score; } public void print(){ System.out.println(this.name+" "+this.gender+" "+this.age+" "+this.score); } public static void main(String[] args) { //直接通过类名访问 System.out.println(Student.classRoom); System.out.println("===="); Student s1 = new Student("张三", "男", 18, 3.8); Student s2 = new Student("李四", "女", 19, 4.0); Student s3 = new Student("王五", "男", 18, 2.6); s1.print(); s2.print(); s3.print(); System.out.println("===="); //也可以通过对象访问,三个对象共享classRoom System.out.println(s1.classRoom); System.out.println(s2.classRoom); System.out.println(s3.classRoom); } }
可以看到,静态成员变量并没有存储到某个具体的对象中。
3.3static修饰成员方法
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何在类外访问呢?
class Stu { private String name; private String gender; private int age; private double score; private static String classRoom="bit110";//private修饰只能在自己类中使用 public Stu(String name,String gender,int age,double score){ this.name=name; this.gender=gender; this.age=age; this.score=score; } } public class TextStu{ public static void main(String[] args) { System.out.println(Stu.classRoom); } }
编译失败,报错:
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
//.... public static String Getclassroom(){ return classRoom; }public class TextStu{ public static void main(String[] args) { System.out.println(Stu.Getclassroom()); } }
静态方法特性:不属于某个具体的对象,是类方法;可以通过对象调用,也可以通过类名.静态方法名(...)方式调用;不能在静态方法中访问任何非静态成员变量;静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用。
3.4 static成员变量初始化
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性,静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
1.就地初始化
就地初始化指的是:在定义时直接给出初始值。
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom = "bit110";
// ...
}
2.静态代码块初始化
看后文---
四、代码块
4.1 代码块概念及分类
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:普通代码块、构造块、静态块和同步代码块。
4.2 普通代码块
普通代码块:定义在方法中的代码块。
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);
}}
4.3 构造代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
class Student { //成员变量 private String name; private String gender; private int age; private double score; public Studenet(){ System.out.println("Student()"); } //实例代码块 { this.name="zhangsan"; this.age=15; this.gender="nan"; System.out.println("实例"); } public void show(){ System.out.println(this.name+" "+this.age+" "+this.gender); } } public class fangfakuai { public static void main(String[] args) { Student stu1=new Student(); stu1.show(); }
4.4 静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
class Student { //成员变量 private String name; private String gender; private int age; private double score; private static String classRoom; public Student(){ System.out.println("Student()"); } //实例代码块 { this.name="zhangsan"; this.age=15; this.gender="nan"; System.out.println("实例代码块"); } //静态代码块 static { classRoom="bit110"; System.out.println("静态代码块"); System.out.println("======"); } public void show(){ System.out.println(this.name+" "+this.age+" "+this.gender); } } public class TextStudent { public static void main(String[] args) { Student stu1=new Student(); Student stu2=new Student(); }
注意:静态代码块不管生成多少个对象,其只会执行一次;静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的;如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行;实例代码块只有在创建对象时才会执行。