目录
【本节目标】
1、掌握类的定义方式以及对象的实例化
2、掌握类中的成员变量和成员方法的使用
3、掌握对象的整个初始化过程
4、掌握封装特性
5、掌握代码块
6、掌握内部类
【正文】
1、面向对象的初步认知
1.1 什么是面向对象
面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。
1.2 面向对象与面向过程
举个例子:洗衣服
以前的方式:
注重的是洗衣服的过程,少了一个环节都不行。按照这种方式写代码,不好维护。
现在的方式:
以面向对象方式来进行处理,就不关注洗衣服的过程,具体操作是通过对象之间的交互来完成的。
2、类定义和使用
对对象(实体)进行抽象(对一个复杂事物的重新认知)
2.1 简单认识类
类是用来对一个实体(对象)来进行描述的
2.2 类的定义格式
在java中定义类时需要用到class关键字:
//创建类
class ClassName{
field; //成员变量
method; //成员方法
}
class为定义类的关键字,ClassName为类的名字,{}中为类的主体。类中一般包含成员变量(字段或者属性)和成员方法(行为)
class WashMachine{
public String brand; // 品牌
public String type; // 型号
public double weight; // 重量
public double lenght; // 长
public double weidth; // 宽
public double height; // 高
public String color; // 颜色
public void WashClothes(){ // 洗衣服
System.out.println("洗衣功能");
}
public void dryClothes(){ // 脱水
System.out.println("脱水功能");
}
public void SetTime(){ // 定时
System.out.println("定时功能");
}
}
采用java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了。
【注】:
- 类名注意采用大驼峰定义
- 成员前写法统一为public,后面会详细解释
- 此处写的方法不带static关键字
3、类的实例化
3.1 什么是实例化
public static void main(String[] args){
//类名 变量 = new 类名() 实例化对象,创建一个对象
Student student = new Student();
public String name;
public int age;
public double score;
public String sex;
}
当成员变量没有赋初值的时候,每个成员变量都是他所对应的0值。
引用类型对应的是null,boolean对应的是false,char对应的是'\u0000'(代表空格)。
【问题】什么是实例化对象?
答:通过new关键字实例化一个对象。只要new就会给这个对象分配一块内存。
【总结1】
- 如何定义一个类?
- class Person{
- public String name;
- }
- 如何通过这个类来实例化对象?
- Person person1 = new Person();
- Person person2 = new Person();
- 怎么去访问实例化出来的对象的方法和属性?
- 通过对象的引用:person1.属性 或者 person1.方法
【注】
- new关键字用于创建一个对象的实例
- 使用 . 来访问对象中的属性和方法
- 同一个类可以创建多个实例
3.2 类和对象的说明
![](https://img-blog.csdnimg.cn/0fec098890514896b3b0a4a4d2373f51.png)
4.this引用
4.1 为什么要有this引用
举一个日期类的例子:
public class Date {
public int year;
public int month;
public int day;
public void setDay(int y, int m, int d){
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
// 对d1,d2,d3的日期设置
d1.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();
d2.printDate();
d3.printDate();
}
}
以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打印,代码整体逻辑非常简单,没有任何问题。
但是细思之下有以下两个疑问:
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
4.2 什么是this引用
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);
}
}
【注】this引用的是调用成员方法的对象。
【总结2】this
- 代表当前对象的引用
- 可以区别,当参数和成员名字冲突的时候
- this本质来说,可以看作一个隐式的参数。
this共有三种应用场景:
1.this.data;//访问成员变量
2.this.func();//访问成员方法
3.this();//调用构造方法
4.3 this引用的特性
5、对象的构造及初始化
5.1 如何初始化对象
public static void main(String[] args) {
int a;
System.out.println(a);
}
// Error:(26, 28) java: 可能尚未初始化变量a
public static void main(String[] args) {
Date d = new Date();
d.printDate();
d.setDate(2021,6,9);
d.printDate();
}
// 代码可以正常通过编译
5.2 构造方法
5.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
}
}
【注】构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
5.2.2 特性
public class Date {
public int year;
public int month;
public int day;
// 无参构造方法
public Date(){
this.year = 1900;
this.month = 1;
this.day = 1;
}
// 带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
public class Date {
public int year;
public int month;
public int day;
/*
Date date(){
}
*/ // 默认提供的
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
public class Date {
public int year;
public int month;
public int day;
// 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
// 此处可以在无参构造方法中通过this调用带有三个参数的构造方法
// 但是this(1900,1,1);必须是构造方法中第一条语句
public Date(){
//System.out.println(year); 注释取消掉,编译会失败
this(1900, 1, 1);
//this.year = 1900;
//this.month = 1;
//this.day = 1;
}
// 带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
【注】:1.this 必须是构造方法中第一段语句
2.不能形成环
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:Error:(19, 12) java: 递归构造器调用
*/
【总结3】
- 构造方法 是没有返回值的方法,方法名和类名是一样的
- 构造方法不止一个,可以有多个,多个构造方法之间构成了重载
- 当我们写了一个类之后,没有写构造方法的时候,编译器会帮我们默认生成一个不带参数的构造方法、
- 当我们写了任何一个构造方法之后,编译器就不再为我们提供不带参数的构造方法
- 一个类至少会有1个构造方法就算你没有写!!!
- 构造方法本质就是实例化对象的时候。(1)分配内存 (2)调用合适的构造方法
- this可以用来调用本类中的其他的构造方法(构造方法当中使用)。必须放在第一行!!!所以,只能在当前构造方法当中,调用一个
- this的用法:(1)this.data; //访问属性 (2)this.func(); // 访问方法 (3)this(); //调用本类中其他的构造方法
- this不能形成环。
5.3 默认初始化
public class Date {
public int year;
public int month;
public int day;
public Date(int year, int month, int day) {
// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public static void main(String[] args) {
// 此处a没有初始化,编译时报错:
// Error:(24, 28) java: 可能尚未初始化变量a
// int a;
// System.out.println(a);
Date d = new Date(2021,6,9);
}
}
Date d = new Date(2021,6,9);
数据类型 | 默认值 |
byte | 0 |
char | '\u0000' |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
5.4 就地初始化
在声明成员变量时,就直接给出了初始值。
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();
}
}
右击鼠标选择“Generate”-->>"toString()",就可以构造方法,而且还可以在main函数中调用并打印出来。此时打印出来的是值,而不是地址,正常来说应该打印地址(名称@哈希值地址),这里是因为toString原码里面重写了该方法,所以打印出值。
6、封装(降低代码的耦合度)
6.1 封装的概念
6.2 访问限定符
![](https://img-blog.csdnimg.cn/84631b62f7a0423393e921250a35430c.png)
public class Computer {
private String cpu; // cpu
private String memory; // 内存
public String screen; // 屏幕
String brand; // 品牌---->default属性
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("上网~~~");
}
}
public class TestComputer {
public static void main(String[] args) {
Computer p = new Computer("HW", "i7", "8G", "13*14");
System.out.println(p.brand); // default属性:只能被本包中类访问
System.out.println(p.screen); // public属性: 可以任何其他类访问
// System.out.println(p.cpu); // private属性:只能在Computer类中访问,不能被其他类访问
}
}
【注】:一般情况下成员变量设置为private,成员方法设置为public。
6.3 封装扩展之包
6.3.1 包的概念
6.3.2 导入包中的类
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
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());
}
}
// 编译出错
Error:(5, 9) java: 对Date的引用不明确
java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配
【注】import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要. import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using
6.3.3 常见的包
1. java.lang: 系统常用基础类 (String 、 Object), 此包从 JDK1.1 后自动导入。2. java.lang.reflflect:java 反射编程包 ;3. java.net: 进行网络编程开发包。4. java.sql: 进行数据库开发的支持包。5. java.util: 是 java 提供的工具程序包。 ( 集合类等 ) 非常重要6. java.io:I/O 编程开发包。
7、static成员
7.1 再谈学生类
![](https://img-blog.csdnimg.cn/b73d1b6b8f364d14bf99e7ce7e3a357e.png)
![](https://img-blog.csdnimg.cn/ce78e029d85c46f38a8b223216f41e84.png)
7.2 static修饰成员变量
【静态成员变量特性】1. 不属于某个具体的对象, 是类的属性,所有对象共享的 ,不存储在某个对象的空间中2. 既可以通过对象访问,也可以通过类名访问,但一般 更推荐使用类名访问3. JDK7 及以前, HotSpot(Java 虚拟机 ) 中存储在方法区, JDK8 及之后,类变量存储在 Java 堆中4. 类变量存储在方法区当中5. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)6.对于第3点,其实方法区和堆属于同一个等级的东西,只不过在JVM当中的实现这个东西的时候,把他放到了堆里。
public class Student{
public String name;
public String gender;
public int age;
public double score;
public static String classRoom = "666";
// ...
public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
System.out.println(Student.classRoom);
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);
// 也可以通过对象访问:但是classRoom是三个对象共享的
System.out.println(s1.classRoom);
System.out.println(s2.classRoom);
System.out.println(s3.classRoom);
}
}
public static void main(String[] args){
Student student1 = null; //student1不指向任何对象
student1.classes = "666"; //这个又属于哪个对象呢?
System.out.println(student.classes); //可以访问,但是不建议,规规矩矩用类名区访问
}
【注】:
- 静态的成员变量和静态的成员方法,都不依赖于对象。因为他们是类变量。
- 普通的成员方法当中,是可以使用静态的成员变量的。
- 静态的成员方法当中,不能访问非静态成员。非静态成员是依赖于对象的。
【静态成员变量特性】1. 不属于某个具体的对象,是类方法2. 可以通过对象调用,也可以通过类名 . 静态方法名 (...) 方式调用,更推荐使用后者3. 静态方法没有隐藏的 this 引用参数,因此不能在静态方法中访问任何非静态成员变量4. 静态方法中不能调用任何非静态方法,因为非静态方法有 this 参数,在静态方法中调用时候无法传递 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
public static String getClassRoom(){
doClass();
return classRoom;
}
// 编译报错:Error:(35, 9) java: 无法从静态上下文中引用非静态 方法 doClass()
8、代码块
8.1 代码块概念以及分类
- 实例代码块/构造代码块
- 静态代码块
- 本地代码块/普通代码块
- 同步代码块(多线程)
8.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);
}
}
// 执行结果
x1 = 10
x2 = 100
8.3 实例(构造)代码块
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) {
Person p1 = new Person();
p1.show();
}
}
// 运行结果
I am instance init()!
I am Person init()!
name: bit age: 12 sex: man
【注】实例代码块优先于构造方法执行,因为编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法第一条语 句前。
8.4 静态代码块
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
//实例代码块
{
this.name = "ft";
this.age = 12;
this.gender = "man";
System.out.println("I am instance init()!");
}
// 静态代码块
static {
classRoom = "666";
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();
}
}
【注】
- 静态代码块不管生成多少个对象,其只会执行一次
静态成员变量是类的属性,因此是在 JVM加载类时开辟空间并初始化的 Java代码在经过编译器编译之后,如果要运行必须先要经过类加载子系统加载到 JVM 中才能运行。 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并,最终放在生成的<> 方法中,该方法在类加载时调用,并且只调用一次。 实例代码块只有在创建对象时才会执行。
静态代码块优先于实例代码块优先于构造方法执行
9、内部类
public class OutClass {
class InnerClass{
}
}
// OutClass是外部类
// InnerClass是内部类
【注】
1. 定义在 class 类名 {} 花括号外部的,即使是在一个文件里,都不能称为内部类public class A{ } class B{ } // A 和 B是两个独立的类,彼此之前没有关系
2. 内部类和外部类共用同一个 java 源文件,但是经过编译之后,内部类会形成单独的字节码文件
9.1 内部类的分类
public class OutClass {
// 成员位置定义:未被static修饰 ---> 普通内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
public void method(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5{
}
}
}
9.1.1 实例内部类(成员内部类)
无static修饰的成员内部类
public class OutClass {
private int a;
static int b;
int c;
public void methodA(){
a = 10;
System.out.println(a);
}
public static void methodB(){
System.out.println(b);
}
// 成员内部类:未被static修饰
class InnerClass{
int c;
public void methodInner(){
// 在内部类中可以直接访问外部类中:任意访问限定符修饰的成员
a = 100;
b =200;
methodA();
methodB();
// 如果外部类和内部类中具有相同名称成员时,优先访问的是内部类自己的
c = 300;
System.out.println(c);
// 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
OutClass.this.c = 400;
System.out.println(OutClass.this.c);
}
}
public static void main(String[] args) {
// 外部类:对象创建 以及 成员访问
OutClass outClass = new OutClass();
System.out.println(outClass.a);
System.out.println(OutClass.b);
System.out.println(outClass.c);
outClass.methodA();
outClass.methodB();
System.out.println("=============内部类的访问=============");
// 要访问普通内部类中成员,必须要创建普通内部类的对象
// 而普通内部类定义与外部类成员定义位置相同,因此创建普通内部类对象时必须借助外部类
// 创建内部类对象
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
// 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建内部类对象
OutClass.InnerClass innerClass2 = outClass.new InnerClass();
innerClass2.methodInner();
}
}
【注】
1. 外部类中的任何成员都可以被在普通内部类方法中直接访问2. 普通内部类所处的成员与外部类成员位置相同,因此也受 public 、 private 等访问限定符的约束3. 在内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问4. 普通内部类对象必须在先有外部类对象前提下才能创建5. 普通内部类的非静态方法中包含了一个指向外部类对象的引用6. 外部类中,不能直接访问内部类中的成员,如果要访问必须先要创建外部类的对象。
9.1.2 静态内部类
有static修饰的成员内部类
public class OutClass {
private int a;
static int b;
public void methodA(){
a = 10;
System.out.println(a);
}
public static void methodB(){
System.out.println(b);
}
// 静态内部类:被static修饰的成员内部类
static class InnerClass{
public void methodInner(){
// 在内部类中只能访问外部类的静态成员
// a = 100; // 编译失败,因为a不是类成员变量
b =200;
// methodA(); // 编译失败,因为methodA()不是类成员方法
methodB();
}
}
public static void main(String[] args) {
// 静态内部类对象创建 & 成员访问
OutClass.InnerClass innerClass = new OutClass.InnerClass();
innerClass.methodInner();
}
}
【注】
1. 在内部类中只能访问外部类中的静态成员2. 创建内部类对象时,不需要先创建外部类对象3. 成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:外部类名称 $ 内部类名称
9.2 局部内部类(基本不用)
public class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
9.3 匿名内部类
在接口处介绍
10、对象的打印
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);
}
}
// 打印结果:day20210829.Person@1b6d3586
要想打印对象内容只需重写toString方法
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;
}
@Override
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]