this关键字(二)
this 回顾
观察代码
public class Dog { String sn; String name; int age; public Dog(){} public Dog(String s,String n,int a){ sn = s; name = n; age = a; } public void sayHi(){ System.out.println("我的名字:"+ name); System.out.println("我的年龄:" + age); } }
以上代码存在什么问题?
前面什么地方用到this?this到底是什么呢?
this主要存在于两个位置:
在构造器中:表示当前被创建的对象
在方法中:哪一个对象调用this所在的方法,此时this就表示哪一个对象
优化后:
/** * 满足封装概念的Dog */ public class Dog { private String sn; private String name; private int age; public void setSn(String sn) { this.sn = sn; } public String getSn() { return sn; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } public Dog(String sn, String name, int age) { this.sn = sn; this.name = name; this.age = age; } public Dog() {} }
this 内存图
编辑
this关键字表示当前对象本身,一般用于类的内部,其内部存在一个地址,指向当前初始化的对象本身。
public class Test01 { public static void main(String[] args) { Dog dog = new Dog(" 旺 财 ", 20); System.out.println("dog = " + dog); } }
当new一个对象时,实际上产生了两个引用,一个是供类Dog内部调用其成员变量和成员方法的this关键字,一个是供外界程序调用实例成员的dog。
this 三种用法
(1)调用成员变量
解决局部变量和成员变量之间的二义性,此时必须使用
调用其他实例方法
同一个类中非static方法间互调(此时可以省略this,但是不建议省略)
public void sayHi(){ // System.out.println("大家好,我叫"+this.name+",我今年"+this.age+"岁"); System.out.println("我的自白:"); this.showInfo(); }
(3)调用本类其他构造方法
this可以调用本类其他构造方法,语法
this(); this(,,,);
注意 : 在构造方法中调用本类其他构造方法必须写到该构造方法第一句,否则出现编译错误。
编辑
一个结合this,满足封装的实战中的Dog类。
/** * 满足封装概念的Dog */ public class Dog { private String sn; private String name; private int age; public void setSn(String sn) { this.sn = sn; } public String getSn() { return sn; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } public Dog(String sn, String name, int age) { //this.sn = sn; // this.name = name; this(sn,name); this.age = age; } public Dog(String sn,String name){ this.sn = sn; this.name = name; } public Dog() {} public void sayHi(){ // System.out.println("大家好,我叫"+this.name+",我今年"+this.age+"岁"); this.showInfo(); } public void showInfo(){ System.out.println("我的自白:"); System.out.println("我的名字:" + this.name); System.out.println("我的年龄:" + this.age); } }
super关键字(二)
回顾 super
回顾之前什么时候使用super:
在子类方法中,调用父类被覆盖的方法,此时必须使用super。
super 关键字
super 关键字表示父类对象,子类要访问父类成员时可以使用super。
super只是一个关键字,内部没有引用(地址)。
super 访问父类非私有字段
System.out.println("我的名字" + super.nick); System.out.println("我的健康值" + super.health); System.out.println("我的亲密度" + super.love); System.out.println("我是一只" + this.strain);
super 访问父类非私有方法
super.print(); System.out.println("我是一只" + this.strain);
super 访问父类构造方法
语法: super(); super(,,,);
例子:
public class Dog extends Pet { String strain; public Dog(String nick,int health,int love,String strain){ super(nick,health,love); this.strain = strain; } 、、、、 }
总结:
super 调用构造方法必须写在子类构造方法的第一句;
如果子类构造方法没有显式调用父类构造方法时,那么jvm会默认调用父类的无参构造super()。
static修饰符
问一个生产车的工厂,一共生产了多少量车?
=> 构成一个车(Car) 的类,统计Car一共创建了多少对象?
=> 紧接着继续思考:统计Car创建了多少对象是不是需要一个变量totalCount?
=> 紧接着继续思考:在哪里声明这个变量totalCount呢?
static
static 关键字表示静态,可以修饰变量构成静态变量,修饰方法构成静态方法。静态变量和静态方法都归类所有,称为类的静态成员,用static关键字修饰。
静态变量
在类中,用static关键字修饰的成员变量称为静态变量,归类所有,也称为类变量,类的所有实例/对象都可以访问,被类的所有实例或对象所共享。
语法 :
static 数据类型 成员变量 [=初始值];
静态变量的访问
类名.静态变量(推荐写法) 对象/实例.静态变量
Car创建了多少量车?
public class Car{ String brand; String type; float price; // 静态变量,归类所有 static int count = 0; public Car(){ //Car.count++; this.count++; } public Car(String brand,String type,float price){ this.brand = brand; this.type = type; this.price = price; // Car.count++; this.count++; } 、、、、 }
类变量/静态变量被类的所有实例或对象所共享
静态方法
static 也可以修饰方法称为静态方法,归类所有,也称类方法。
语法
[修饰符] static 返回值类型 方法名(形参列表){ }
静态方法访问方式
类名.静态方法() 对象.静态方法()
静态方法特性
静态方法中可以访问静态变量和类的其他静态方法
实例方法中可以访问静态成员(静态变量和静态方法);静态方法不能访问实例成员
public class Car { static int num = 0; public Car(){ Car.num++; //num++; <==> this.num++; //this.num++; } public static void test(){ System.out.println("test"); } public static int getNum(){ // System.out.println(this.count); // this.showInfo(); Car.test(); return Car.num; } public void showInfo(){ // 访问静态变量 // System.out.println(Car.num); // 访问静态方法 Car.getNum(); } }
jvm加载 static 成员的过程
当加载一个类到jvm的静态区时,首先jvm会扫描xx.class中的静态成员并分配空间且初始化。
当通过xx.class new一个对象时,可以在该对象的实例方法中访问静态成员;反之静态方法中不能访问实例成员。
编辑
final修饰符
final 表示最终的意思,可以修饰类、方法、局部变量、成员变量。
最终类
final 修饰类表示最终类。
public final class Car extends MotoVehicle { }
最终类不能被继承。
最终方法
如果一个方法被final修饰,称为最终方法。
public final void test() { System.out.println("test"); }
最终方法不能被重写。
常量
final修饰的局部变量称为常量,常量只能赋值一次,不能再重新赋值。
基本数据类型:表示的值不能改变
引用数据类型:所引用的地址值不能改变
(1)final修饰基本数据类型
final int a = 10; // error // a = 20
(2)final修饰引用数据类型
// 常引用 // final 修饰引用数据类型 final Car car = new Car(); System.out.println(car); car.setBrand("Benz"); car.setType("X5"); car.setBrand("Audi"); car.setBrand("A4"); // car 被final修饰,不能再用于指向其他堆空间 //car = new Car(); //System.out.println(car);
代码块
{} 标记的代码称为代码块,根据其位置的不同可以分为普通代码块、构造代码块、静态代码块、同步代码块。
普通代码块
普通代码块{},一般存在于方法中,形成作用域。
作用域特性:
作用域可以嵌套,内层作用作用域可以访问外层作用域的变量
当访问一个变量时,首先在变量所在的作用域查找,如果能找到,停止查找并输出变量内容;当本 作用域没找到时,尝试去上一次作用域查找,依次类推。这个过程形成的查找链称为作用域链。
public class Dog { String nick; int health; int love; String strain; int count0 = 0; public void showInfo(){ int count1 = 10; // 普通代码块 { // int count1 = 100; int count2 = 20; System.out.println(count2); System.out.println(count1); // System.out.println(count0); System.out.println(this.count0); } } 、、、 }
构造代码块
构造代码块在类中(类的内部)、方法外。
构造代码块构造一个对象执行一次,在构造方法前执行。
public class Car{ private String brand; private String type; private float price; // 构造代码块 { System.out.println("构造代码块..."); } public Car(){} public Car(String brand,String type,float price{ System.out.println("Car(String,String,float)"); this.brand = brand; this.type = type; this.price = price; } }
如果需要在构造方法执行前加载一些资源(读取配置文件、xml文件等)时使用。我们把构造对象前的 一切操作都可以放到构造代码块中去执行。
静态代码块
被static关键字修饰的代码块称为静态代码块。 静态代码块位于类的内部、方法的外部。
静态代码块只执行一次,在构造代码块、构造方法前执行。
当类的字节码被加载到内存时,此时程序需要加载一些资源(读取资源文件等),可以使用静态代码块, 此时被加载进来的资源一般都可以被多个实例所共享。
内部类
public class Car{ String brand; String type; float price; static int count; // 静态代码块 static{ System.out.println("静态代码块..."); count = 0; } // 构造代码块 { System.out.println("构造代码块..."); } public Car(){} public Car(String brand,String type,float price){ System.out.println("Car(String,String,float)"); this.brand = brand; this.type = type; this.price = price; } }
类的组织方式
(1)类和类之间平行关系
一个文件可以定义多个类,但只能存在一个public类,且文件的名字和public类的名字保持一致。
public class Dog { } class Penguin{ }
编译完成后
Dog和Penguin 地位一样,和分开定义成两个文件一样。
编辑
(2)类和类之间包含关系
public class Outer { public class Inner{ } }
编译完成后
编辑
Outer和Inner是包含关系。Outer称为外部类,Inner称为内部类。
内部类Inner作为一个Outer的成员而存在。
内部类概述
什么是内部类,把一个类定义在另一个类的内部,把里面的类称之为内部类,把外面的类称之为外部类。
编辑
内部类可以看作和字段、方法一样,是外部类的成员,而成员可以有static修饰。
静态内部类:使用static修饰的内部类,那么访问内部类直接使用外部类名来访问
实例(成员)内部类:没有使用static修饰的内部类,访问内部类使用外部类的对象来访问
局部(方法)内部类:定义在方法中的内部类,一般不用
匿名内部类:特殊的局部内部类,适合于仅使用一次使用的类
对于每个内部类来说,Java编译器会生成独立.class文件。
静态和实例内部类:外部类名$内部类名字
局部内部类:外部类名$数字内部类名称
匿名内部类:外部类名$数字
成员内部类
内部类Inner作为外部类的一个成员而存在,Inner称为成员内部类。
public class Outer { [修饰符] class Inner{ } }
一般而言,成员内部类的访问修饰符是默认访问权限(包访问权限),开发时,可以根据需要添加具体的访问权限。
创建成员内部类对象
public class Test01 { public static void main(String[] args) { // [1] 创建外部类对象 Outer outer = new Outer(); // [2] 创建内部类对象 Outer.Inner inner = outer.new Inner(); inner.showInfo(); } }
成员内部类特性
成员内部类可以直接访问外部类的私有成员
public class Outer { private String name = "Outer"; class Inner{ public void showInfo() { System.out.println("inner:showInfo()"); // [1]访问外部类的私有成员System.out.println(name); } } }
静态内部类
成员内部类如果被static修饰变成静态内部类,作为外部类的一个静态成员而存在。
public class Outer { static class Inner{ } }
创建静态内部类对象
public class Test01 { public static void main(String[] args) { // 1.创建静态内部类对象 Outer.Inner inner = new Outer.Inner(); inner.showInfo(); } }
静态内部类特性
静态内部类可以访问外部类的静态私有成员
public class Outer { private static String name = "Outer"; static class Inner{ public void showInfo() { System.out.println("static inner:showInfo()"); System.out.println(name); } } }
方法内部类
当一个类存在于方法中时,构成方法内部类。方法内部类只能存在于方法中,同时也只能在方法中创建对象,
方法内部类前没有任何修饰符,因为内部类定义在方法中,都是局部存在的。
方法内部类特性
方法内部类可以读取方法的局部变量,但不能修改。
public class Outer { public void print(int count) { int a = 10; class Inner{ public void showInfo() { System.out.println("print():showInfo()"); // System.out.println(a); // System.out.println(count); // 方法中的局部变量只能读,不能被方法内部类修改 // count = 1000; } } // 只要在showInfo访问了a或者count,a和count就被加final修饰,在方法内部类外都不能 a = 20; count = 1000; // 创建方法内部类对象 //Inner inner = new Inner(); //inner.showInfo(); // 匿名对象 new Inner().showInfo(); } }
匿名内部类
当一个类只使用一次,可以声明成匿名内部类匿名内部类 必须有实现存在。
public class Outer { public void print(){ /* class Inner implements AInterface{ @Override public void showInfo() { System.out.println("inner.showInfo"); } } new Inner().showInfo(); */ /*AInterface aInterface = new AInterface(){ @Override public void showInfo() { System.out.println("inner.showInfo"); } }; aInterface.showInfo();*/ // 通过匿名类创建匿名对象 new AInterface(){ @Override public void showInfo() { System.out.println("inner.showInfo"); } } }
枚举类
枚举的诞生史
在服装行业,衣服的分类根据性别可以表示为三种情况:男装、女装、中性服装。
private ? type; public void setType(? type){ this.type = type }
定义一个变量来表示服装的分类?请问该变量的类型使用什么?
使用int和String类型,且先假设使用int类型,因为分类情况是固定的,为了防止调用者乱创建类型,可以把三种情况使用常量来表示。
public class ClothType { public static final int MEN = 0; public static final int WOMEN = 1; public static final int NEUTRAL = 2; }
注意:
常量使用final修饰,并且使用大写字母组成,如果是多个单词组成,使用下划线分割。
此时调用setType方法传递的值应该是ClothType类中三个常量之一。但是此时依然存在一个问题
——依然可以乱传入参数比如100,此时就不合理了。
同理如果使用String类型,还是可以乱设置数据。那么说明使用int或String是类型不安全的。那么如果使用对象来表示三种情况呢?
public class ClothType { public static final ClothType MEN = new ClothType(); public static final ClothType WOMEN = new ClothType(); public static final ClothType NEUTRAL = new ClothType(); }
此时调用setType确实只能传入ClothType类型的对象,但是依然不安全,为什么?因为调用者可以自行创建一个ClothType对象,如:setType(new ClothType())。
此时为了防止调用者私自创建出新的对象,我们把CLothType的构造器私有化起来,外界就访问不了了,此时调用setType方法只能传入ClothType类中的三个常量。此时代码变成:
public class ClothType { public static final ClothType MEN = new ClothType(); public static final ClothType WOMEN = new ClothType(); public static final ClothType NEUTRAL = new ClothType(); private ClothType() {} }
高,实在是高!就是代码复杂了点,如果存在定义这种类型安全的且对象数量固定的类的语法,再简单点就更好了——有枚举类。
枚举类的定义和使用
枚举是一种特殊的类,固定的一个类只能有哪些对象,定义格式:
public enum 枚举类名{ 常量对象A, 常量对象B, 常量对象C; }
我们自定义的枚举类在底层都是直接继承了java.lang.Enum类的。
public enum ClothType { MEN, WOMEN, NEUTRAL; }
枚举中都是全局公共的静态常量,可以直接使用枚举类名调用。
ClothType type = ClothType.MEN;
因为java.lang.Enum类是所有枚举类的父类,所以所有的枚举对象可以调用Enum类中的方法.
String name = 枚举对象.name(); // 返回枚举对象的常量名称 int ordinal = 枚举对象.ordinal(); // 返回枚举对象的序号,从0开始
int ordinal = 枚举对象.ordinal(); // 返回枚举对象的序号,从0开始
注意:枚举类不能使用创建对象
public class EnumDemo { public static void main(String[] args) { int ordinal = ClothType.MEN.ordinal(); String name = ClothType.MEN.name(); System.out.println(ordinal); System.out.println(name); new ClothType(); //语法报错 } }
以上就是Java入门第十天的全部内容了。
资料文档地址:Java开发零基础篇:day10面向对象(四).pdf