2

构造函数,构造代码块,静态代码块
构造函数
格式:类名(参数1,参数2,…){构造函数执行语句};
关于构造函数,以下几点要注意:
1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的 。
2.构造函数的作用是用于给对象进行初始化 。
3.它可以重载,可以被权限修饰符修饰,但是它没有返回值 。
4.当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造方法 。
6.定义构造函数的需求性:当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中 。
构造代码块
格式:{构造代码块执行语句};
关于构造代码块,以下几点要注意:
1.构造代码块的作用是给对象进行统一初始化 。
2.对象一建立就运行构造代码块了,而且优先于构造函数执行 。
3.构造代码块与构造函数的区别是:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,
静态代码块
格式:{静态代码块执行语句};
关于静态代码块,要注意的是:
1.它是随着类的加载而执行,只执行一次,并优先于主函数 。
2.静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的 。区分着理解比较容易记牢 。
3.无法在静态方法里引用实例变量、也无法调用实例方法,它是随着类的加载而执行,此时还没有对象,但是可以调用静态变量和静态方法,你甚至可以在静态代码块里调用main方法,都是没有问题的
2.无法在静态方法里使用this关键字和super关键字(因为this关键字指向该方法所属的对象,而静态方法是属于类级的,不存在对象一说;至于super关键字,只要不是用在构造方法里,那么
它 就是指向父类对象的,而静态方法是不能引用实例对象的,因此也不能使用super关键字)
3.无法在静态方法里声明其他静态变量(其实这一点不只是静态方法才适用,包括实例方法也无法在方法体中声明静态变量,因为静态变量属于类变量)
4.无法在静态方法里使用域修饰符来声明变量:、、,只能使用默认的访问域(这一点实例方法 也是适用的 )
静态修饰符是一个修饰符,用于修饰成员(成员变量和成员函数)静态成员随着类的加载而加载 。静态成员优先于对象存在 。静态成员被所有对象所共享静态成员多了一个中调用方式,可以被类名直接调用 。
静态使用的注意事项 。
静态方法只能访问静态成员, 非静态方法既可以访问静态又可以访问非静态 。静态方法中不可以定义this,super关键字 。因为this代表是对象 。而静态存在时,有可能没有对象 。所以静态方法运行时,this是没有任何对象代表的 。
简单说,先进内存的数据不可以访问后进内存的数据,可是后进内存数据可以访问先进内存的数据 。主函数是静态的
静态的优缺点
优点: 静态成员多了一种调用方式 。可以直接被类名调用 格式 :类名.静态成员 。也可以被对象调用 。
弊端 : 静态方法只能访问静态成员,出现了访问局限性 。
静态修饰的数据对象共享的数据,存放在方法区的静态区中 。
非静态的数据,是每一个对象特有数据 。存放在对象的所属的堆内存中 。
当成员变量被静态修饰后,和非静态成员变量的区别:
静态变量也称为类变量,也就是直接可以被类名调用的变量 。这个变量是所属于类的 。
非静态变量成为成员变量,或者实例变量,是被对象调用的,是所属具体对象的 。静态变量随着类的加载而加载,也意味着随着类的消失而消失 。生命周期最长 。
实例变量,随着对象的创建而加载,随着对象的消失而消失 。按照对象的生命周期而存在 。静态变量存储在方法区的静态区中 。
实例变量存在于对象所属的堆内存中 。静态变量数据,被所有对象所共享 。
实例变量是对象中的特有数据
对象的创建过程
p= new ("gao",24);这个简单的语句会涉及到如下几个步骤:
1,由于是要创建类对象,java虚拟机(JVM)先去找.class文件,如果有的话,将其加载到内存 。
2,将类型信息(包括静态变量,方法等)加载进方法区 。
3,执行该类中代码块,如果有的话,对.class类进行初始化 。
4,到这时才进行堆内存空间的开辟,并为对象分配地址 。
5,在堆内存中建立对象的成员属性,并对其进行初始化(先进行默认初始化再进行显示初始化 。)
6,进行构造代码块的初始化,由此看出构造代码库初始化的优先级要高于对象构造函数的初始化 。
7,对象的构造函数进行初始化 。
8,将堆内存中的地址赋给栈内存中的p变量 。
匿名对象(没有名字的对象):
new Car(); //匿名对象其实就是定义对象的简写格式 。
Car c = new Car();
c.run();
用匿名对象来书写以上代码:
new Car().run();
匿名对象的使用方法: 1 当对象对方法仅进行一次调用的时候,就可以简化成匿名对象 。
如一个 对象需要进行调用方法2次,用匿名对象的
new Car().run()

2

文章插图
new Car().run()
这是2个对象分别调用了run(),不是一个对象调用了多方法 。
2 匿名对象可以作为实际参数进行传递 。
void show(Car c)
//......
show(new Car());
匿名对象的内存分析:
2

文章插图
内部类
内部类的学习分为以下10个知识点:
1.创建内部类与连接外部类
2.内部类与向上转型
3.局部内部类
4.匿名内部类
5.局部内部类和匿名内部类
6.嵌套类
7.内部类的继承
8.内部类可以被覆盖吗
9.为什么需要内部类
10.内部类标识符
1、创建内部类与连接外部类
public class Car {private int speed = 100;class Tyre {int getSpeed() { //访问外部类成员return speed;}Car getCar() { //通过.this获取外部类对象return Car.this;}}public static void main(String[] args) {Car car = new Car();Car.Tyre tyre = car.new Tyre();tyre.getSpeed();System.out.println(car == tyre.getCar()); // true}}
【2】要想创建内部类的对象,必须使用外部类的对象,如上,且必须地具体指明这个对象的类型:.,然后通过 .new () 来创建内部类对象 。
内部类对象会暗暗连接到创建它的外部类对象上,捕获外部类的对象引用,然后在内部类中访问此外部类的成员时,就是通过这个引用来访问的,并且拥有其外部类的所有元素的访问权 。正是由于此原因,中非静态内部类创建静态实例才会造成内存泄漏 。
在内部类中通过 .this 可获取外部类对象,如上代码中通过 Car.this 获取 Car 类对象 。
2、内部类与向上转型
当将内部类向上转型为基类,尤其是转型为一个接口的时候,此内部类能够隐藏这个接口的实现,如下例:
interface cost{double getPrice();}
public class Car {private int speed = 100;class Tyre implements cost{double originalPrice = 100;@Overridepublic double getPrice() {return originalPrice * 0.75;}}public static void main(String[] args) {Car car = new Car();cost tyre = car.new Tyre(); //向上转型tyre.getPrice();// 75.0}}
3、局部内部类
前面提到的都是处于外部类中的内部类,而内部类也可以定义在一个方法里面或者在任意的作用域中,这么做有以下两个理由:
1.如实现某接口的内部类,可以在方法中创建并返回对其的引用
2.要解决一个复杂的问题,需要创建一个类来辅助,但不希望这个类是公共可用的
例1:在方法中定义内部类:
public class Car {private int speed = 100;cost getTyre() { //方法内部class Tyre implements cost {double originalPrice = 100;@Overridepublic double getPrice() { //打折操作return originalPrice * 0.75;}}return new Tyre();}public static void main(String[] args) {Car car = new Car();cost tyre = car.getTyre();tyre.getPrice();// 75.0}}
例2:在任意作用域中定义内部类:
public class Car {private boolean hadTyre = true; //是否有轮胎cost getTyre() {if (hadTyre) { //任意作用域中class Tyre implements cost {double originalPrice = 100;@Overridepublic double getPrice() { //打折操作return originalPrice * 0.75;}}return new Tyre();}else{return null;}}public static void main(String[] args) {Car car = new Car();cost tyre = car.getTyre();tyre.getPrice();// 75.0}}
定义在一个方法里面或者在任意的作用域中的内部类也叫作局部内部类,局部内部类不能有等访问说明符,因为它不是外部类的一部分,但是它可以访问当前代码块内的常量以及外部类中的所有成员:
public class Car {private boolean hadTyre = true;private double coupon1 = 10; // 外部类成员(10元优惠券)cost getTyre() {if (hadTyre) {double coupon2 = 5; // 代码块内常量(5元优惠券)class Tyre implements cost {double originalPrice = 100;@Overridepublic double getPrice() {return originalPrice * 0.75 - coupon1 - coupon2;}}return new Tyre();} else {return null;}}public static void main(String[] args) {Car car = new Car();cost tyre = car.getTyre();System.out.print(tyre.getPrice());// 60.0}}
书中说可以访问当前代码块内的常量,这里有点不解,是常量?
4、匿名内部类
上例中 () 方法要创建一个 cost 对象,从 cost tyre = car.(); 看出我们并不关心内部类的名字 Tyre,只要返回的是 cost 类对象就足够了,所以这里可以用匿名内部类来实现,顾名思义,它没有名字,如下例:
public class Car {cost getTyre(double p) {return new cost() {double coupon = 10; // 10元优惠券private double price = p; //原价@Overridepublic double getPrice() {return price - coupon;}};}public static void main(String[] args) {Car car = new Car();cost tyre = car.getTyre(100);System.out.print(tyre.getPrice());// 90.0}}
你可能会有疑问,这段代码可以编译通过吗?应该是 (finalp) 吧?确实,在匿名内部类中使用一个在其外部定义的对象,那么编译器必须要求其参数引用是 final 类型,以上代码在低于 Java 8 的版本编译不会通过,但是在Java 8 版本不用 final 修饰局部变量也可以编译通过,只不过不能修改值,只能打印输出或赋值给其他变量 。
5、局部内部类和匿名内部类
首先来看局部内部类和匿名内部类的对比实现:
public class Car {private double coupon2 = 5;cost getTyre1(double p) { //局部内部类class Tyre implements cost {double coupon = 10;private double price = p;@Overridepublic double getPrice() {return price - coupon - coupon2;}}return new Tyre();}cost getTyre2(double p) { //匿名内部类return new cost() {double coupon = 10;private double price = p;@Overridepublic double getPrice() {return price - coupon - coupon2;}};}}
() 方法用来创建一个 cost 类对象,我们分别使用局部内部类和匿名内部类实现了这个功能,它们具有相同的行为和能力,既然局部内部类的名字在方法外是不可见的,那为什么我们仍然使用局部内部类而不是匿名内部类呢?唯一的理由是:我们需要一个已命名的构造器,或者需要重载狗仔器,而匿名内部类只能用于实例初始化,所以使用局部内部类而不使用匿名内部类的另一个理由就是:需要不止一个该内部类的对象 。
6、嵌套类
如果不需要内部类对象与其外部类对象之间有联系,那么可以将内部类声明为,即静态内部类,也称嵌套类,静态内部类和非静态内部类的最大区别就是非静态内部类对象隐士的保存了一个外部类对象的引用,这意味着:
1.不需要外部类的对象就可以创建静态内部类的对象
2.不能从静态内部类的对象中访问非静态的外部类对象
3.静态内部类中可以定义静态或者非静态的成员,而非静态内部类则不能有静态成员 。
public class Car {private static double price = 100; static class Tyre implements cost {private static double coupon = 5; // 3.若为非静态内部类则无法定义为 static 类型@Overridepublic double getPrice() {return price; // 2.若 price 不为 static 则无法访问}}public static void main(String[] args) {Car.Tyre tyre = new Car.Tyre(); // 1.静态内部类的创建不依赖外部类对象}}
这也是静态内部类和内部类的关键区别 。此外静态内部类也可定义在接口内部:
interface cost {class Price { } // 默认为 public 、static}
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所公用,那么使用接口内部嵌套类会显得很方便 。
7、内部类的继承
怎么继承自一个内部类?内部类的构造必须依赖其外部类对象,所以在继承内部类的时候,事情会变得复杂,比如我们要继承自 Size 类:
2

文章插图
class Tyre{class Size{}}
可以这样写:
public class TyreSize extends Tyre.Size{}
编译器会报错: Noof type ‘com..Tyre’ is in scope,即缺少 Tyre 类的实例,若要创建位于 Tyre 内部的 Size 类,则必须要有 Tyre 的实例对象,要解决这个问题,需要引入 Tyre 实例且说清它们之间的关联:
public class TyreSize extends Tyre.Size{TyreSize(Tyre tyre) {tyre.super();}}
编译通过 。
8、内部类可以被覆盖吗
如果创建了一个内部类,然后继承其外部类并重新定义此内部类时,内部类可以被覆盖吗?例如:
class Car {Car() {System.out.println("new Car()");new Tyre();}class Tyre { // 我会被覆盖吗Tyre() { System.out.println("new Tyre()"); }}}public class BigCar extends Car {class Tyre {Tyre() {System.out.println("BigCar new Tyre()"); }}public static void main(String[] args) {new BigCar();}}
在 Car 的构造器中新建的 Tyre 是 Car 中的 Tyre 还是中的 Tyre 呢?运行程序输出:
new Car()
new Tyre()
中定义的 Tyre 内部类并没有覆盖 Car 中的 Tyre 内部类,实际上这两个内部类是完全独立的两个实体,各自在自己的命名空间内,没有谁覆盖谁之说 。
9、为什么需要内部类
也就是说,内部类存在的意义是什么呢?为什么 Sun 公司如此费心地增加这项语言特性呢?这里将内部类的意义总结为以下四点:
A. 逻辑上被包含且对外隐藏
如果一个类在逻辑上被包含于另一个类,那么将此类设置为另一个类的内部类,比如轮胎类可以写作汽车类的内部类:
public class Car {public class Tyre{}}
上面代码中只存在被包含关系,也可通过组合方式实现,写作内部类是没有必要的,当想对外保密汽车使用何种轮胎时,写作内部类才是有必要的:
public class Car {private class Tyre{}}
B. 实现多重继承
每个内部类都能独立的继承一个接口,无论外部类是否已经继承了某个接口的实现,对于内部类都没有影响 。内部类提供可以继承多个抽象类或具体的类的能力,有效的实现了多重继承,网上一个实例简单直观的展示了通过内部类实现多重继承(儿子利用多重继承来继承父亲和母亲的优良基因):
public class Father { //父亲public int strong(){return 9;}}public class Mother { //母亲public int kind(){return 8;}}
public class Son { // 儿子通过内部类实现多重继承class Father_1 extends Father{public int strong(){return super.strong() + 1;}}class Mother_1 extendsMother{public int kind(){return super.kind() - 2;}}public int getStrong(){return new Father_1().strong();}public int getKind(){return new Mother_1().kind();}}
C. 闭包与回调
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域 。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外部类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外部类对象的引用,在此作用域内,内部类有权操作所有的成员,包括成员 。
通过内部类提供闭包功能比指针更灵活、更安全
例如:一个接口程序员和一个基类作家都有一个相同的方法work,相同的方法名,但是其含义完全不同,这时候就需要闭包 。
class Writer { //作家基类void work(){}}interface programmer{ //程序员接口void work();}
闭包实现代码如下:
public class WriterProgrammer extends Writer {@Overridepublic void work(){//写作}public void code(){//写代码}class ProgrammerInner implements programmer{@Overridepublic void work(){code();}}}
继承自,直接实现父类作家的work()方法,然后使用内部类实现程序员的work()方法回调code()方法 。如果 同时继承自且实现接口,那么就不能同时实现作家和程序员的意义不同的 work()方法:
class WriterProgrammer extends Writer implements programmer{@Overridepublic void work() { //programmer的 work}}
D. 控制框架
应用程序框架就是被设计用以解决某类特定问题的一个类或一组类,而控制框架就是一类特殊的应用程序框架,它用来解决响应事件的需求,主要用来响应事件的系统被称作事件驱动系统 。Java Swing 库就是一个控制框架,它优雅的解决了 GUI 的问题,并使用了大量的内部类
控制框架的完整实现是由单个类创建的,内部类用来表示解决问题所必须的各种不同的,另外内部类能够很容易的访问外部类的任意成员,可以让这种实现更轻松 。
例如控制温室的运作:控制灯光、水、温度调节器的开关等,每个行为都是完全不同的,使用内部类可以在单一的类中产生对同一个基类 Event 的多种导出版本,对于温室系统的每一种行为,都继承一个新的 Event 内部类,并在要实现的 () 中编写控制代码:
public class GreenhouseControls{public class LightOn extends Event {public void action() {//开灯...}}public class LightOff extends Event {public void action() {//关灯...}}public class WaterOn extends Event {public void action() {//开水闸...}}// 其他操作...}
10、内部类标识符
每个类都会产生一个.class文件,其中包含了如何创建该类型的对象的全部信息,内部类也必须生成一个 .class 文件,从而可以包含它自己的 Class 对象信息,这些类文件的命名有严格的规则:外部类名字+“$”+ 内部类名字,例如:
class Car {class Tyre {}}
生成的 .class 文件包括:
Car.class
Car$Tyre.class
如果内部类是匿名的,编译器会简单的产生一个数字作为其标识符,如果内部类是嵌套在别的内部类之中,只需直接将它们的名字加在其外部类标识符与”$”后面,这是 Java 的标准命名方式,产生的文件自动都是平台无关的 。
泛型 泛型有三种使用方式,分别为:泛型类(含成员变量)、泛型接口、泛型方法
一个最普通的泛型类:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型//在实例化泛型类时,必须指定T的具体类型public class Generic{ //key这个成员变量的类型为T,T的类型由外部指定private T key;public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定this.key = key;}public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定return key;}}
//定义一个泛型接口public interface Generator {public T next();}
/**
* 这才是一个真正的泛型方法 。
* 首先在与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:K ( ){ * ... * } */
T ( ){
.out.(" key :" + .()); //当然这个例子举的不太合适,只是为了说明泛型方法的特性 。
T test = .();
test;
list