1 this关键字
定义:
this代表当前类的对象(谁调用我我就代表谁),记录的是对象的地址。
应用:
(1)访问对象的成员(包括属性、方法);
(2)调用类的构造方法;
引言
一个类可以创建无数个对象,无数个对象都去调用一个构造方法,那么如果不指名到底是哪个对象,这样就会造成问题,因为构造方法并不知道到底是给哪个对象进行初始化的,所以java提供了this来对对象进行标识。
(1)this(参数):就会调用对应的构造方法
(2)this可以标识成员变量,可以进行省略,但是如果当局部变量和成员变量同名时,就必须要明确this.来标识是成员变量。
1.1 案例
public class Person {
// 属性
String name;
int age;
// 无参构造方法
public Person(){
}
// 带参构造方法一(一出生就有名字)
public Person(String name){
// 调用无参构造方法,可省略不写,但要定义在第一行
this();
// this.name代表成员变量,name代表局部变量
this.name = name;
}
// 带参构造方法二(一出生就有名字和年龄)
public Person(String name,int age){
// 调用带参构造方法一:
this(name);
this.age = age;
}
// 成员方法一
public void speak(){
//局部变量要进行初始化
String name = "小红";
int age = 66;
//(1)当方法中定义了变量时,this.name代表成员变量,age代表局部变量
//(2)当方法中没有定义变量,this可以省略不写,this下不屑均代表成员变量
System.out.println("name="+name+",age="+age);
}
// 成员方法二
public void call(){
//调用成员方法一
this.speak();
//在一个方法中,调用其他方法时,也是有this的指向,但此处this可以省略不写
}
}
public class PersonDemo {
public static void main(String[] args) {
// 创建对象
Person p1 = new Person("coco",18);
Person p2 = new Person("xiaoran",28);
// 调用方法
p1.speak();
p2.speak();
}
}
堆栈运行图:
初始化过程文字描述:
(1)new Person()生成一个首地址值,main方法压栈,p1=0x0011
(2)new Person(“coco”,18)对象调用带有两个参数的构造方法(this=0x0011),方法进栈先执行this(name),又调用带有一个参数的构造方法(this=0x0011)。
(3)this.name指的是对象里面的name成员变量。继续执行则this.name=name=”coco”,方法弹栈。在继续执行this.age=age=18,含有两个参数的构造方法弹栈。
1.2 注意事项
(1)一般方法中不能够调用构造方法,而构造方法中可以调用一般方法。
一般方法中不能调用构造方法,是因为构造方法是创建对象时只调用一次对对象进行初始化。构造方法中可以调用一般方法,是因为构造方法进行初始化时需要某些功能(也可以理解为对构造方法中的部分代码也进行封装)。
(2)调用构造方法的语句要放在构造方法第一行,因为要先完成初始化对象。
(3)一个构造方法中不能同时调用两个构造方法(即一个构造方法只能有一个this)。
(4)两个构造方法之间不能互相调用。
(5)自己调用自己,进入死循环,报错。
2 static关键字
引言
public class Static_introduce {
// 属性
String name;
int age;
// 构造方法
public Static_introduce(String name, int age) {
this.name = name;
this.age = age;
}
// 一般方法一
public void speak() {
System.out.println("name=" + name + "age=" + age);
}
// 一般方法二
public static void sleep() {
System.out.println("天黑了,该睡觉了");
}
}
public class Static_introduce_demo {
public static void main(String[] args) {
// 创建对象
Static_introduce person = new Static_introduce("coco",18);
//对象调用方法
person.speak();
person.sleep();
}
}
对封装类方法的调用时,speak()方法调用了对象内部的成员变量,成员变量随着对象的产生而产生,因此需要创建对象对象名调用方法。创建对象是为了产生实例,并进行数据的封装,而调用sleep()方法时却没有使用到对象封装的数据,
那么对象创建还有意义吗?
—答案肯定是没有意义的。虽然创建对象编译运行都没有问题,但是会在堆内存中创建对象,空间较为浪费。因为封装在对象中的数据一个都没有进行使用,所以不建议创建对象。
java就给出了一个解决办法:使用静态static关键字。
2.1 static调用方法
被静态修饰的方法有两种调用方法:
(1)可以被对象调用(不建议使用)
Person.sleep();
(2)可以被类名调用
Static_introduce.sleep();
2.2 static的使用
static用于修饰可供外界无需实例化直接使用的方法或属性。
2.3 静态成员特点
(1)静态成员函数只属于类,随着类的加载而加载,随着类的消失而消失。
(2)静态方法不能够访问非静态成员,但是非静态成员中可以访问静态。
原因是优先级,静态是先于对象存在。(静态方法无法访问后面创建的对象中的数据,所以静态无法书写非静态。而非静态成员可以通过创建对象调用或者类名直接调用。)
(3)在静态方法中不允许出现this,super关键字
因为这时候对象还有可能不存在,this没有任何的指向。
(4)静态成员被所有的对象共享使用。
2.4 静态优缺点
(1)弊端在于访问出现了局限性
(2)好处是静态成员可以由类调用,被所有对象共享,节省空间。
2.5 成员变量和静态变量的区别
(1)所属的范围(名称)不同
静态变量(类变量)所属于类
成员变量(实例变量)所属于对象
(2)内存存储区域不同
静态存储在方法区中,也称为数据共享区
成员变量存储在堆内存中
(3)加载时期不同
静态变量随着类的加载而加载
成员变量随着对象的加载而加载
(4)调用不同
静态变量可以被对象和类调用(一般都是类名调用)
成员变量只能够被对象调用
2.6 案例(获取圆的面积)
public class Circle {
// 属性私有化
private double radius;
// 3,14是一个固定不变的值,可以在类中定义一个常量。
// 每次创建对象都会出现pi占据内存空间,因此可以使用static对成员修饰。
private static double pi = 3.14;
// 构造方法
public Circle(double radius) {
this.radius = radius;
}
// 行为
public double getArea() {
// this和Circle可以忽略不写
return this.radius * this.radius * Circle.pi;
}
}
public class CircleDemo {
public static void main(String[] args) {
// 创建对象
Circle c = new Circle(5.5);
// 调用获取面积方法getArea();并输出
double area = c.getArea();
System.out.println(area);
}
}
内存图:
(1)运行程序后执行CircleDemo类,在方法区开辟一块区域,该区域会有一个当前类默认的无参构造方法
(2)执行main方法,存在于静态区中,main方法进栈。
(3)先加载Circle类在创建(new)对象。在方法区开辟一块专门存放Circle类的区域。执行Circle类内部的成员和属性。成员变量radius、构造方法Circle(double radius)和成员方法getArea()存放在Circle类中。而静态变量pi存放静态区中。
(4)创建Circle对象。先在堆中开辟一块区域,自动生成一个首地址值0x0011。有参构造方法Circle(double radius)进栈,this指向该对象,对对象进行初始化,初始化完成后出栈。之后main方法中引用变量c指向该对象,这样对象就不会被回收。
(5)调用getArea()方法,方法进栈,执行后出栈。
补充:
(1)方法区是存在的,堆栈是调用,会进行方法进栈弹栈或者对象回收机制。
(2) Circle类中3,14是一个固定不变的值,可以在类中定义一个常量。
由于每次创建对象都会出现pi都会出现在堆中,占据内存空间,因此可以使用static对成员修饰。使其跟随类的加载而出现,就可以实现对对象数据共享。
3 静态代码块
格式:
static{
//静态代码块执行的区域
}
作用:随着类的加载而加载,只执行一次,用于给类进行初始化。
作用方式:静态代码块是在静态变量显示初始化之后再执行的
应用场景:类不需要创建对象,但是需要初始化。
class Base {
static {
System.out.println("a");
}
}
public class StaticBase {
static {
System.out.println("b");
}
public static void main(String[] args) {
new Base();
new Base();
}
static {
System.out.println("c");
}
static {
System.out.println("d");
}
}
输出结果:b c d a
static是指创建对象调用类一个static静态代码块只执行一次。
4 构造代码块
格式:
{
//代码
}
作用:给对象初始化,每创建一次对象都会被调用。可以把不同构造方法中相同共性的内容定义在里面。
执行顺序上,构造代码块优先于构造方法。其次,构造代码块是给所有对象进行统一初始化,而构造方法是给对应的对象初始化。
public class StaticExpend {
public static void main(String[] args) {
new Demo06(4);
}
}
class Demo06 {
int num = 8;
Demo06() {
System.out.println("a");
}
Demo06(int x) {
System.out.println("b");
}
static {
System.out.println("c");
}
{
System.out.println("d" + this.num);
}
}
输出结果:c d8 a