目录
this是“成员方法”第一位隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
1、面向对象
1.1、什么是面向对象
Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来设计程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好.
1.2、面向对象和面向过程的区别
1.面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
2.面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
2、类
1、类就是一类对象的统称。对象就是这一类具体化的一个实例,通过关键字new 实例化对象
2、声明一个类就是自己定义一个类型(自定义类型),而类在 Java 中属于引用类型, Java 使用关键字 class 来声明类。
基本语法
// 创建类
class ClassName{
field; // 字段(属性) 或者 成员变量
method; // 行为 或者 成员方法
}
- class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
- 类中的元素称为:成员属性。类中的函数称为:成员方法。
- 类名注意采用大驼峰
class Date{
//成员变量 成员属性 实例变量
//字段-》属性-》成员变量->类的内部 方法的外部的-》1、普通成员变量 静态成员变量
public int year;
public int month;
public int day;
//成员方法->行为->1、普通成员方法 静态成员方法
public void Print(){
System.out.println("年月日");
}
}
- 一般 一个文件当中只定义一个类
- public 修饰的类必须和文件相同
- 不要在 代码直接修改 public 修饰的类 ,要通过开发工具(重命名)
2.1、字段/属性/成员变量
类中方法外部定义的变量. 这样的变量我们称为 "字段" 或 "属性" 或 "成员变量"(三种称呼都可以, 一般不会严格区分)
注意事项
- 使用 . 访问对象的字段.
- "访问" 既包含读, 也包含写.
- 对于一个对象的字段如果没有显式设置初始值, 那么会被设置一个默认的初值
成员变量默认值:
基本类型 | 默认值 |
boolean | false |
char | \u0000 |
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
成员变量和局部变量的区别
区别 | 成员变量 | 局部变量 |
类中位置不同 | 类中方法外 | 类中方法里 |
内存中位置 | 堆内存 | 栈内存 |
生命周期不同 | 随对象存在而存在 | 随方法调用而存在,结束而释放 |
初始化值不同 | 默认初始值 | 没有默认初始值,必须定义并赋值 |
2.2 方法
用于描述一个对象的行为.
class student{
//字段-》属性-》成员变量->类的内部 方法的外部的
public string name;
public int age;
//成员方法->行为->1、普通成员方法
public void show() {
System.out.println("我叫" + name + ", 今年" + age + "岁");
}
class Test {
public static void main(String[] args) {
Person person = new Person();
person.show();
}
}
2.3 null 空引用
null 在 Java 中为 "空引用", 表示不引用任何对象. 类似于 C 语言中的空指针. 如果对 null 进行 . 操作就会引发异常.
class Person {
public String name;
public int age;
}
class Test {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name.length()); // 获取字符串长度
}
}
// 执行结果
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:9
- 当一个引用 赋值为 null —> 这个对象不指向任何对象;
- 引用不能指向引用,而是指向 另一个引用指向的对象;
- 一个引用只能指向一个对象;
2.4、类的实例化(对象)
定义了一个类,就相当于在计算机中定义了一种新的类型(自定义类型);
用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。
// 创建类
class <class_name>{
field;//成员属性
method;//成员方法
}
// 实例化对象
<class_name> <对象名> = new <class_name>()
注意事项
- new 关键字用于创建一个对象的实例.
- 使用 . 来访问对象中的属性和方法.
- 同一个类可以创建对个实例(一对多)
3、this 关键字
3.1 this引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。
this 代表的就是 当前对象的引用,在new 对象是开辟内存时,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引用的是调用成员方法的对象。
3.2 为什么使用 this
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
- 成员变量自己赋值自己、 优先局部变量,错误赋值
- 使用this 指名赋值对象成员变量
public void setDay(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
3.3 this 引用的特性
- this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
- this只能在"成员方法"中使用 (静态不能使用)
- 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
this是“成员方法”第一位隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
this 访问构造方法
- this 必须在第一行
- 必须在构造方法内部
4、 对象的构造及初始化
4.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
}
}
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间
4.2 特性
- 名字必须与类名相同,修饰符 必须是 public
- 没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
- 写了 有参构造,向在调用无参构造,必须自己写出来
- 只要有自定义的构造方法,系统就不会在提供默认构造
- 如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的;定义了有参构造,但是没有写无参构造,new一个无参,编译不通过,报错
- 构造方法中,可以通过this调用其他构造方法来简化代码
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;
//有参构造
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;
// 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
// 此处可以在无参构造方法中通过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;
}
}
注意:
- this(...)必须是构造方法中第一条语句
- 不能形成环
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:Error:(19, 12) java: 递归构造器调用
*/
4.3 初始化
1. 默认初始化:不给参数,成员会被赋 初始值(基本类型),自定义类型,会调用构造方法
2. 就地初始化:就地初始化,就是在 声明成员变量是直接给了初始值
5、封装
面向对象(OOP)的三个基本特征:封装、继承、多态
封装:就是将数据和操作方法 隐藏起来,不让外部直接访问,必须通过公开外部接口对对象交互访问
规则:
- 将类内部的某些数据方法隐藏起来,不允许直接访问
- 只能通过类提供接口进行 交互
实现:
- 修改属性私有(private)
- 创建 set/get 方法(public修饰,提供外部使用)
- 在set/get 方法中设置条件进行判断限制
class Person {
private String name;
private int age;
public Person(){
}
public void setage(int age){
if(age>30){
this.age = 18;
}else{
this.age = age;
}
}
public int gettage(){
return age;
}
}
访问限定符
范围 | private | default | protected | public |
同一包中的同一类 | ✓ | ✓ | ✓ | ✓ |
同一包中的不同类 | ✓ | ✓ | ✓ | |
不同包中的子类 | ✓ | ✓ | ||
不同包中的非子类 | ✓ |
6、static关键字
6.1 static修饰成员变量
static修饰的成员变量,称为静态成员变量/类变量
静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
在类中非静态方法定义,局部变量定义不能被 static 修饰
【静态成员变量特性】
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,一般更推荐使用类名访问,对象指向空,也可以调用静态变量
- 类变量存储在方法区当中;
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
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);
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);
}
}
class TestDemo{
public int a;
public static int count;
}
public class Main{
public static void main(String[] args) {
TestDemo t1 = new TestDemo();
t1.a++; // 1
TestDemo.count++; // 1
System.out.println(t1.a);
System.out.println(TestDemo.count);
System.out.println("============");
TestDemo t2 = new TestDemo();
t2.a++; // 1
TestDemo.count++; // 2 静态的 累计相加 属于类
System.out.println(t2.a);
System.out.println(TestDemo.count);
}
}
count被static所修饰,所有类共享。且不属于对象,访问方式为:类名 . 属性。
6.2 static修饰成员方法
被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。
静态成员变量一般是通过静态方法来访问的。
【静态方法特性】
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用
- 不能在静态方法中直接访问任何非静态成员变量
//静态成员方法
public static String getClassRoom(){
//不能引用 非静态变量 this
System.out.println(this);
return classRoom;
}
// 编译失败:Error:(35, 28) java: 无法从静态上下文中引用非静态 变量 this
public static String getClassRoom(){
age += 1;
return classRoom;
}
// 编译失败:Error:(35, 9) java: 无法从静态上下文中引用非静态 变量 age
静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
//静态成员方法
public static String getClassRoom(){
//不能引用 非静态 方法
doClass();
return classRoom;
}
7、代码块
普通代码块:定义在方法中的代码块
构造代码块:定义在类中的代码块(不加修饰符)
- 也称 实例代码块 ,构造代码块一般用于初始化实例成员变量
静态代码块:定义在类中的 被 static 修饰的代码块
一般用于初始化静态成员变量
注意:
- 静态代码块 不管 new 了多少实例对象,只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的;
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
- 实例代码块只会在 创建对象时执行
class Student{
public String name = "liuhuangshu";
public int age;
public static String classRoom;
// 静态代码块 只会执行一次
static {
//初始化 静态成员变量
classRoom = "java1";
System.out.println("静态代码块 初始化静态的数据成员");
}
//构造代码块 在构造方法前执行
{
name = "caocao";
System.out.println("非静态代码块/实例代码块/构造代码块 初始化非静态的数据成员");
}
public Student(){
System.out.println("无参的构造!");
}
public Student(String name,int age){
this.name = name;
this.age = age;
System.out.println("带有两个参数的构造!");
}
public void show(){
{
System.out.println("普通代码块");
}
System.out.println("name:"+name+" age:"+age);
}
}
public static void main(String[] args) {
Student student1 = new Student();
System.out.println("===============");
Student student2 = new Student();
student1.show();
}
总结
1、代码块执行顺序
静态代码块(只执行一次) —> 构造代码块(new 对象执行)—> 构造方法 —>方法中的 普通代码块
2、构造代码块 和 构造方法的区别
构造代码块:只要 new 对象,有参、无参 都会执行 此代码块
构造方法:new 对象,只会调用 匹配的代码块
8、内部类
在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现
- 静态内部类
- 实例内部类(非静态内部类)
注意:
- 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类;
- 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
8.1 静态内部类
被static修饰的内部成员类称为静态内部类
class OuterClass1 {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
//静态内部类
static class staticInClass{
public int date4 = 4;
private int date5 = 5;
public static int date6 = 6;
//
public void func(){
System.out.println("staticInClass->func");
OuterClass1 outerClass1 = new OuterClass1();
//静态 内部类 不能调用外部类非静态 成员变量
System.out.println(outerClass1.date1);
System.out.println(outerClass1.date2);
System.out.println(date3);
System.out.println(date4);
System.out.println(date5);
System.out.println(date6);
}
}
//外部类
public void func(){
System.out.println("OuterClass1->func");
//外部类 不能调用内部类非静态 成员变量
staticInClass staticInClass = new staticInClass();
System.out.println(date1);
System.out.println(date2);
System.out.println(date3);
System.out.println(staticInClass.date4);
System.out.println(staticInClass.date5);
//外部类 可以直接调用 内部类的静态成员
System.out.println(staticInClass.date6);
}
}
注意:
1. 静态内部类 不能调用 外部类的非静态成员
2. 静态内部类 未实例化 外部类不能调用非静态成员
3. 静态内部类初始化:
public static void main(String[] args) {
//初始化 内部静态类
OuterClass1.staticInClass staticInClass = new OuterClass1.staticInClass();
staticInClass.func();
}
注意:
- 静态内部类中只能访问外部类中的静态成员,想访问非静态,在静态内部类中实例化外部类
- 创建静态内部类对象时,不需要先创建外部类对象
8.2 非静态内部类
未被static修饰的成员内部类
package inclass;
class OuterClass2 {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
//非静态内部类
class InterClass{
public int date4 = 4;
private int date5 = 5;
//非静态内部类 需定义静态常量
//外部类加载时,不会加载非静态内部类
//常量是在编译时就确定的
public static final int date6 = 6;
//非静态内部类
public void func(){
System.out.println("InterClass->func");
OuterClass2 outerClass2 = new OuterClass2();
// 内部类 不能调用外部类非静态 成员变量
System.out.println(outerClass2.date1);
System.out.println(outerClass2.date2);
System.out.println(date3);
System.out.println(date4);
System.out.println(date5);
System.out.println(date6);
}
}
//外部类
public void func2(){
System.out.println("OuterClass2->func");
//外部类 不能调用内部类非静态 成员变量
InterClass InterClass = new InterClass();
System.out.println(date1);
System.out.println(date2);
System.out.println(date3);
System.out.println(InterClass.date4);
System.out.println(InterClass.date5);
//外部类 可以直接调用 内部类的静态成员
System.out.println(InterClass.date6);
}
}
1. 非静态内部类 不能定义静态成员变量 和 静态方法;
因为static类型的属性和方法,在类加载的时候就会存在于内存中,但非静态内部类不随外部类加载,只有在外部类实例化是加载;
2. 可以为变量 加上final 修饰为常量,常量是编译时的阶段确定的
方法加上 final 表示此方法就一条,不能被重写
实例化 非静态内部类: 非静态内部类依赖于外部类
想访问非静态内部类 的成员,需先实例化外部类
public class Test2 {
public static void main(String[] args) {
System.out.println(OuterClass2.InterClass.date6);
}
public static void main1(String[] args) {
//想访问非静态内部类 的成员,需先实例化外部类
//1.这种写法不太好
//OuterClass2.InterClass interClass = new OuterClass2().new InterClass();
//2.先实例化 外部类 在实例化内部类
OuterClass2 outerClass2 = new OuterClass2();
OuterClass2.InterClass interClass = outerClass2.new InterClass();
interClass.func();
}
}
注意:
- 外部类中的任何成员都可以在非静态内部类方法中直接访问
- 非静态内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 非静态内部类对象必须在先有外部类对象前提下才能创建
- 非静态内部类的非静态方法中包含了一个指向外部类对象的引用(两个 this,同名优先指向自己,访问外部 需 外部类名.this.同名成员)
- 外部类中,不能直接访问非静态内部类中的成员,如果要访问必须先要创建内部类的对象
class OuterClass3{
public int date = 1 ;
class InterClass2{
public int date = 11111;
public void func(){
System.out.println("interClass2->func()");
System.out.println(this.date); //就近原则
System.out.println(OuterClass3.this.date);
}
}
}
public class Test3 {
public static void main(String[] args) {
OuterClass3 outerClass3 = new OuterClass3();
OuterClass3.InterClass2 interClass = outerClass3.new InterClass2();
interClass.func();
}
}
// interClass2->func()
// 11111
// 1
8.3 匿名内部类
匿名内部类 直接调用 相当与无名,只能用一次
interface ITest{
void func();
}
public class Test4 {
public static void main(String[] args) {
//匿名内部类
new ITest(){
@Override
public void func() {
System.out.println("匿名内部类");
}
}.func(); //直接调用
}
}