三、面向对象
Object:对象,东西,一切皆对象 = = 啥都是东西
面向对象核心:封装、继承、多态。
3.1类
1)类:是同类型东西的概念,是对现实生活中事物的描述,映射到Java中描述就是class定义的类。
类是对象的模板、图纸,是对象的数据结构定义。简单说就是“名词”。
2)其实定义类,就是在描述事物,就是在定义属性(变量)和方法(函数)。
3)类中可以声明:属性,方法,构造器;
属性(变量)分为:实例变量,局部变量;
实例变量:用于声明对象的结构的,在创建对象时候分配内存,每个对象有一份!
实例变量(对象属性)在堆中分配,并作用于整个类中,实例变量有默认值,不初始化也能参与运算;
局部变量:局部变量在栈中分配,作用于方法或语句中,必须初始化,有值才能运算。
4)类与类之间的关系:
①关联:一个类作为另一个类的成员变量:需要另一个类来共同完成。
class A {
pulic B b
}
class B {
}
②继承:
class B extends A {
}
class A {
}
③依赖:个别方法和另一个类相关。
class A {
public void f(B b) {//参数里有B
}
public B g() { //返回值里有B
}
}
class B {
}
5)null与空指针异常:
引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象。
当某个引用类型变量为null时无法对对象实施访问(因为它没有指向任何对象)。
此时,如果通过引用访问成员变量或调用方法,会产生NullPointerException空指针异常。
注意事项:除了8中基本类型,其他都是引用类型变量(也叫句柄)。
3.2对象
对象:是这类事物实实在在存在的个体!利用类做为模板创建的个体实例,本质是数据。
匿名对象:
使用方式一:当对对象的方法只调用一次时,可用匿名对象来完成,这样比较简化。如果对一个对象进行多个成员调用,则必须给这个对象起个名字。
使用方式二:可以将匿名对象作为实际参数进行传递。
3.3包
1)包名必须是小写,多个单词用“.”隔开。在同一个包中,不能有同名的类!
2)只要在同一个包中,则可直接用extends(类型互知道在哪),若不在同一个包中,则用import导入。
3.4方法及其调用
方法是用于对当前对象数据进行算法计算,实现业务功能。
方法是对象的功能,对象的动作,对象的行为。总之是动词!
方法名没有规定,建议首单词为小写动词,其他单词首字母大写。
必须定义返回值!可有无参数方法。
方法调用只有两种方式:
①对象引用调用
②类名调用(即静态类时)。
3.5引用
是对个体的标识名称。
1)是代词,是对象的引用,就像拴着对象的绳子。
2)引用本身不是对象!引用指代了对象!
3)引用的值是对象的地址值(或叫句柄),通过地址值引用了对象。
4)引用的值不是对象!
注意事项:“.”叫取成员运算,可以理解为“的”。
3.6访问控制(封装)
封装:将数据封装到类的内部,将算法封装到方法中。
1)封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。
通常有两种访问方式:set 设置,get 获取。
2)封装结果:存在但是不可见。
3)public:任何位置可见,可以修饰:类、成员属性、成员方法、内部类、跨包访问类(需要使用import语句导入),成员属性 = = 成员变量。
4)protected:当前包中可见,子类中可见。可以修饰:成员属性、成员方法、内部类(只能在类体中使用,不能修饰类)。
5)默认的:当前包内部可见,就是没有任何修饰词,可以修饰:类、成员属性、成员方法、内部类,但在实际项目中很少使用。
默认类(包内类)的访问范围:当前包内部可见,不能在其他包中访问类,访问受限!main方法若定在默认类中JVM将找不到,无法执行,因此必定在public类中。
6)private:仅仅在类内部可见。可以修饰:成员属性、成员方法、内部类(只能在类体中使用,不能修饰类)。
私有的方法不能继承,也不能重写。
注意事项:在企业项目中建议:所有类都是公用类。封装的类使用内部类!
3.7构造器
用于创建对象并初始化对象属性的方法,叫“构造方法”,也叫“构造器”;构造器在类中定义。
1)构造器的名称必须与类名同名,包括大小写。
2)构造器没有返回值,但也不能写void,也不能写return。
3)构造器的参数:一般是初始化对象的前提条件。
4)用new调用!且对象一建立,构造器就运行且仅运行一次。一般方法可被调用多次。
5)类一定有构造器!这是真的,不需要质疑!
6)如果类没有声明(定义)任何的构造器,Java编译器会自动插入默认构造器!
7)默认构造是无参数,方法体是空的构造器,且默认构造器的访问权限随着所属类的访问权限变化而变化。
如,若类被public修饰,则默认构造器也带public修饰符。
8)默认构造器是看不到的,一旦自己写上构造器则默认构造器就没有了。
自己写的叫自定义构造器,即便自己写的是空参数的构造器,也是自定义构造器,而不是默认构造器。
9)如果类声明了构造器,Java编译器将不再提供默认构造器。
若没手动写出无参构造器,但却调用了无参构造器,将会报错!
eg:默认构造器
public class Demo {
public static void main(String[] args) {
Foo foo = new Foo(); //调用了javac自动添加的默认构造器!
//Koo koo = new Koo();//编译错误,没有Koo()构造器
Koo koo = new Koo(8);
}
}
class Foo { //Foo有构造器,有无参数的默认构造器!
}
class Koo {
public Koo(int a) { //声明了有参数构造器
System.out.println("Call Koo(int)");
}
}
10)构造器是可以重载的,重载的目的是为了使用方便,重载规则与方法重载规则相同。
11)构造器是不能继承的!虽说是叫构造方法,但实际上它不是常说的一般方法。
12)子类继承父类,那么子类型构造器默认调用父类型的无参数构造器。
13)子类构造器一定要调用父类构造器,如果父类没有无参数构造器,则必须使用super(有参数的),来调用父类有参的构造器。
那么,为什么子类一定要访问父类的构造器?
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,
所以子类在对象初始化时,要先访问一下父类中的构造器。
总之,子类中至少会有一个构造器会访问父类中的构造器,且子类中每一个构造函数内的第一行都有一句隐式super()。
3.8 super()、super.和 this()、this.
1)this:在运行期间,哪个对象在调用this所在的方法,this就代表哪个对象,隐含绑定到当前“这个对象”。
2)super():调用父类无参构造器,一定在子类构造器第一行使用!如果没有则是默认存在super()的!这是Java默认添加的super()。
3)super.是访问父类对象,父类对象的引用,与this.用法一致
4)this():调用本类的其他构造器,按照参数调用构造器,必须在构造器中使用,必须在第一行使用,this() 与 super() 互斥,不能同时存在
5)this.是访问当前对象,本类对象的引用,在能区别实例变量和局部变量时,this可省略,否则一定不能省!
6)如果子父类中出现非私有的同名成员变量时,子类要访问本类中的变量用this. ;子类要访问父类中的同名变量用super. 。
eg1:方法参数传递原理 与 this关键字
eg2:this. 和 this()
Cell c = new Cell();
System.out.println(c.x + ","+c.y);
class Cell {
int x;
int y;
public Cell() {
this(1,1);//调用本类的其他构造器
}
public Cell( int x, int y) {
this.x = x ; this.y = y;
}
}
eg3:super()
class Xoo{
public Xoo(int s) {
System.out.println("Call Xoo(int)");
}
} //super()用于在子类构造器中调用父类的构造器
class Yoo extends Xoo{
public Yoo() {//编译错误,子类调用不到父类型无参数构造器
}
public Yoo(){
//super(); //编译错误,子类调用不到父类型无参数构造器
super(100); //super(100) 调用了父类 Xoo(int) 构造器
}
}
3.9重载和重写
1)重写:通过类的继承关系,由于父类中的方法不能满足新的要求,因此需要在子类中修改从父类中继承的方法叫重写(覆盖)。
①方法名、参数列表、返回值类型与父类的一模一样,但方法的实现不同。
若方法名、参数列表相同,但返回值类型不同会有编译错误!
若方法名、返回值类型相同,参数列表不同,则不叫重写了。
②子类若继承了抽象类或实现了接口,则必须重写全部的抽象方法。若没有全部实现抽象方法,则子类仍是一个抽象类!
③子类重写抽象类中的抽象方法或接口的方法时,访问权限修饰符一定要大于或等于被重写的抽象方法的访问权限修饰符!
④静态方法只能重写静态方法!
2)重载:方法名一样,参数列表不同的方法构成重载的方法(多态的一种形式)。
①调用方法:根据参数列表和方法名调用不同方法。
②与返回值类型无关。
③重载遵循所谓“编译期绑定”,即在编译时根据参数变量的类型判断应调用哪个方法。
eg:重载
int[] ary1 = {'A','B','C'};
char[] ary2 = {'A', 'B', 'C'};
System.out.println(ary1);//println(Object) //按对象调用,结果为地址值,没有println(int[])
System.out.println(ary2); //println(char[]) ABC
System.out.println('中'); //println(char) 中
System.out.println((int)'中');//println(int) 20013
3.10继承
父子概念的继承:
圆继承于图形,圆是子概念(子类型 Sub class)图形是父类型(Super Class也叫超类),
继承在语法方面的好处:子类共享了父类的属性和方法的定义,子类复用了父类的属性和方法,节省了代码。
1)继承是is a :“是”我中的一种,一种所属关系。
2)子类型对象可以赋值给父类型变量(多态的一种形式),变量是代词,父类型代词可以引用子类型东西。
3)继承只能是单继承,即直接继承,而非间接继承。
因为多继承容易带来安全隐患,当多个父类中定义了相同功能,当功能内容不同时,子类无法确定要运行哪一个。
4)父类不能强转成子类,会造型异常!子类向父类转化是隐式的。
5)只有变量的类型定义的属性和方法才能被访问!见下例。
6)重写遵循所谓“运行期绑定”,即在运行的时候根据引用变量指向的实际对象类型调用方法。
eg:Shape s,s只能访问Shape上声明的属性和方法
Circle c = new Circle(3,4,5);
Shape s = c; //父类型变量s引用了子类型实例 -- s 和 c引用了同一个对象new Circle(3,4,5)
s.up();
System.out.println(c.r);
System.out.println(c.area());
//System.out.println(s.area());//编译错误
//System.out.println(s.r); //在Shape上没有定义r属性!
7)引用类型变量的类型转换 instanceof
public static void main(String[] args) {
Circle c = new Circle(3,4,5);
Rect r = new Rect(3,4,5,6);
Shape s = c;
Shape s1 = r;
//Circle x = s; //编译错误,父类型变量不能赋值给子类型
Circle x = (Circle)s;//正常执行
//Circle y = (Circle)s1;//运行异常,类型转换异常
//instaceof instace:实例 of:的
//instaceof 运算 检查变量引用的对象的类型是否兼容
//s引用的是圆对象,s instanceof Circle 检查s引用的对象是否是Circle类型的实例!
System.out.println(s instanceof Circle);//true
System.out.println(s1 instanceof Circle);//false
test(c); test(r);
}
public static void test(Shape s){//多态的参数
if(s instanceof Circle){//实现了安全的类型转换 --if(s instanceof Circle)保护了(Circle)s不会出现异常
Circle c = (Circle) s;
System.out.println("这是一个圆, 面积"+c.area());
}
if(s instanceof Rect){
Rect r = (Rect) s; System.out.println("这是一个矩形, 面积"+r.area());
}
}
8)继承时候对象的创建过程
①Java首先递归加载所有类搭配方法区。
②分配父子类型的内存(实例变量)。
③递归调用构造器。
9)重写方法与重载方法的调用规则
10)属性绑定到变量的类型,由变量类型决定访问哪个属性;
方法动态绑定到对象,由对象的类型决定访问哪个方法。
(强转对方法动态绑定到对象无影响,因为强转的是父类的引用,而实例是没变的,只是把实例当作另一个状态去看而已。
但是强转对属性动态绑定到变量类型有影响。)其他解释请看多态部分!
eg1:方法动态绑定到运行期间对象的方法 实例1
eg2:方法动态绑定到运行期间对象的方法 实例2
11)为何查阅父类功能,创建子类对象使用功能?
Java中支持多层继承,也就是一个继承体系。
想要使用体系,先查阅父类的描述,因为父类中定义的是该体系中共性的功能,通过了共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了,
然而在具体调用时,要创建最(低)子类的对象。
原因如下:①因为父类有可能不能创建对象
②创建子类对象,可以使用更多的功能,包括基本的也包括特有的。
12)属性无继承概念,所以你有你的,我有我的,各自调用各自的,不影响,即使子父类中有同名属性也无影响。
eg:子父类同名属性无影响
class Base {
public static final String FOO="foo";
public static void main(String[] args){
Base b=new Base();
Sub s=new Sub();
Base.FOO; //foo
b.Foo; //foo
Sub.Foo;//bar
s.Foo;//bar
( (Base) s . Foo);// foo
}
}
class Sub extends Base{
public static final String FOO="bar";
}
3.11 static
静态的,只能在类内部使用,可以修饰:属性,方法,内部类。在类加载期间初始化,存在方法区中。
1)静态成员随着类的加载而加载,加载于方法区中,且优先于对象存在。
2)静态修饰的成员:属于类级别的成员,是全体类实例(所有对象)所共享。
3)静态属性:只有一份(而实例变量是每个对象有一份),全体实例共享,类似于全局变量。
4)使用类名访问静态变量,以及类名直接调用方法,不需要创建对象。
5)静态方法只能访问静态成员(静态属性和静态方法),非静态方法既可访问静态,也可访问非静态。
6)静态方法中没有隐含参数this,因此不能访问当前对象资源。也不能定义this和super关键字,因为静态优于对象先存在。
7)非静态方法省略的是this,静态方法省略的是类名(在同一类中),即直接使用属性和方法。
8)静态方法一般用于与当前对象无关工具方法,工厂方法。如:Math.sqrt() Arrays.sort()
9)静态代码块:随着类的加载而执行(用到类的内容才叫加载,只有引用是不加载的),且只执行一次,且优先于主函数,用于给类初始化。
10)代码块(构造代码块):给所有对象进行统一初始化,且优先于构造器执行;而构造器是给对应的对象进行初始化。
eg:静态代码块与代码块(构造代码块)
class Goo{
{//代码块(构造代码块),在创建对象时候执行!类似于构造器的作用
System.out.println("HI");
}
static{//静态代码块,在类的加载期间执行,只执行一次
System.out.println("Loading Goo.class");
}
}
public static void main(String[] args) {
Point p1 = new Point(3,4);
Point p2 = new Point(6,8);
//在对象上调用方法,当前对象隐含传递给隐含参数this
System.out.println(p1.distance(p2));//distance(p1,p2)
double d = Point.distance(p1, p2);
System.out.println(d); //5
//静态方法调用时候不传递隐含的当前对象参数
}
class Point{
int x; int y;
public Point(int x, int y) {
this.x = x; this.y = y;
}
//静态方法中没有隐含参数this!在静态方法中不能访问this的属性和方法!
public static double distance(Point p1, Point p2){
int a = p1.x -p2.x;
int b = p1.y -p2.y;
return Math.sqrt(a*a + b*b);
}
/** 计算当前点(this)到另外一个点(other)的距离 */
public double distance(/*Point this*/ Point other){
int a = this.x - other.x;
int b = this.y - other.y;
double c = Math.sqrt(a*a + b*b); return c;
}
}
11)对象的创建过程及顺序:
Person P = new Person( “chang” , 23) ; 这句话都做了什么事情?
①因为new用到了Person.class,所以会先找到Person.class文件加载到内存中。
②执行该类中的static代码块(如果有的话),给Person类.class类进行初始化。
③在堆内存中开辟空间,分配内存地址,栈内存中开辟空间也就有了。
④在堆内存中建立对象的特有属性,并进行默认(隐式)初始化。
⑤对属性进行显式初始化。
⑥对对象进行构造代码块初始化。
⑦对对象进行对应的构造器初始化。
⑧将内存地址赋给栈内存中的P变量。
3.12 final
最终的,可以修饰:类、方法、变量(成员变量和局部变量)。
1)final 修饰的类:不能再继承。
2)final修饰的方法:不能再重写。
3)final 的方法和类,阻止了动态代理模式!动态代理模式广泛的应用在: Spring Hibernate Struts2
4)企业编程规范:不允许使用final 的方法和类!
5)final的变量:final变量只能初始化一次(赋值一次,且方法中不能有给final变量赋值的语句!因为方法可被调用多次!),
不能再修改!也可在方法的参数列表中添加final。
eg1:final的局部变量
final int a;
a = 5;//第一次叫初始化!不是赋值
//a = 8;//编译错误
public static void test(final int a, int b){
//a++;//编译错误,不能再修改
System.out.println(a);
}
eg2:final的数组
final String[] ary={"A","B"}; //ary:数组变量,ary[0]--数组元素
ary[0]="Tom";//数组元素可以修改
//ary=null;//数组变量不能修改
eg3:final的实例变量
public static void main(String[] args) {
Dog d1 = new Dog();
Dog d2 = new Dog();
//d1.id = 8;//每个实例的id 不可以再修改
System.out.println(d1.id+","+d2.id+","+Dog.numOfDogs);
}
class Dog{
final int id;//实例变量,每个对象一份,不能再次修改
static int numOfDogs=0;//静态,只有一份
public Dog() { id = numOfDogs++;
}
}
6)static final 共同修饰的叫常量,常量:
public static final double PI = 3.14; //PI 是直接数的代名词,是名字。
字面量(==直接量):直接写出数值 3.1415926535897 宏观说:字面量和常量都称为常量!
3.13多态
继承体现了多态:父类型变量可以引用各种各样的子类型实例,也可接收子类对象。
个体的多态:父类型的子类型实例是多种多样的。
行为的多态:父类型定义方法被子类重写为多种多样的,重载也是多态的方法。
1)千万不能出现将父类对象转成子类类型,会造型异常!
2)多态前提:必须是类与类之间有关系。要么继承,要么实现。通常还有一个前提:存在覆盖。
3)多态的好处:多态的出现大大的提高程序的扩展性。
4)多态的弊端:虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员。
5)在多态中成员函数的特点:
①在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
②在运行时期:参阅对象所属的类中是否有调用的方法。
③简单总结就是:成员方法在多态调用时,编译看左边,运行看右边。
6)在多态中,成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类)。
7)在多态中,静态成员方法和属性的特点:无论编译和运行,都参考做左边。
8)父类引用指向子类对象,当父类想使用子类中特有属性、方法时,要向下转型。
3.14抽象类
抽象就是将拥有共同方法和属性的对象提取出来,提取后,重新设计一个更加通用、更加大众化的类,就叫抽象类。
1)abstract关键字可以修饰类、方法,即抽象类和抽象方法。
2)抽象类可以有具体的方法,或者全部都是具体方法,但一个类中只要有一个抽象方法,那么这个类就是抽象类,并且必须用abstract修饰类。
3)抽象类可以被继承,则子类必须实现抽象类中的全部抽象方法,否则子类也将是抽象类。抽象类也可主动继承实体类。
4)抽象类不能实例化,即不能用new生成实例。
5)可以声明一个抽象类型的变量并指向具体子类的对象。
6)抽象类可以实现接口中的方法。
7)抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
3.15接口
interface 差不多 = = abstract class
1)接口是like a :“像”我中的一种,是继承体系之外的,用于功能扩展!想扩展就实现,不想就不用实现。
2)接口中只能声明抽象方法和常量且声明格式都是固定的,只不过可以省略。
eg:接口中声明常量和抽象方法的格式是固定的
interface Runner {
/*public abstract final*/ int SPEED=100;//声明常量
/*public abstract 省略了,写也对*/ void run();//声明抽象方法
}
3)接口中的成员不写修饰符时,默认都是public。
4)接口不能有构造器,因为不能实例化何以初始化,接口只能被“实现”。
5)具体类实现了一个接口,则必须实现全部的抽象方法,若没有全部实现,则该类为抽象类。所以说,接口约定了具体类的方法,约定了类的外部行为。
6)具体类可以同时实现多个接口,就是多继承现象。
7)多重继承:class Cat implements Hunter , Runner
--> Cat 即是Hunter 也是 Runner。
8)接口用 implements 表实现,实际是继承关系,可有多个接口(实现),继承用 extends 只能有一个继承关系。
9)一个类既可以继承的同时,又“实现”接口:class A extends B implements C , D
10)类与类之间是继承关系,类与接口之间是实现关系,接口与接口之间是继承关系,且只有接口之间可以多继承,
即:interface A{},interface B{},interface C extends A , B
但接口多继承时要注意,要避免A、B接口中有方法名相同、参数列表相同,但返回值类型不相同的情况,因为被具体类实现时,不确定调用哪个方法。
11)abstract class和interface有什么区别。
①从语法角度:abstract class方法中可以有自己的数据成员,也可以有非abstract的成员方法,并赋予方法的默认行为,
而在interface方式中一般不定义成员数据变量,所有的方法都是abstract,方法不能拥有默认的行为。
②从编程的角度:abstract class在java语言中表示的是一种继承关系,一个类只能使用一次继承关系。而一个类可以实现多个interface。
③从问题域角度:abstract class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,
即父类和派生类在概念本质上应该是相同的。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,
仅仅是实现了interface定义的契约而已。
3.16内部类
当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容。
在类内部定义的类为成员内部类,在方法里定义的类为局部内部类,被static修饰的为静态内部类。一个类中可有多个内部类。
1)内部类主要用于,封装一个类的声明在类的内部,减少类的暴露。
2)内部类的实例化:实例化时不需要出写对象,非要写的话为:new 外部类名.内部类名();而不是外部类名.new 内部类名()。
3)内部类的访问规则:内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,
格式:外部类名.This即下面第4条。外部类要访问内部类,必须建立内部类对象。
4)当内部类定义在外部类的成员位置上,而且非私有,则在外部其他类中可以直接建立内部类对象。
格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer().new Inner();
5)当内部类在外部类的成员位置上,就可以被成员修饰符所修饰。比如private:将内部类在外部类中进行封装。
6)静态内部类:被static修饰后就具备了静态的特性。当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
①在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();
②在外部其他类中,如何直接访问static内部类的静态成员呢?
Outer.Inner.function();
注意事项:
① 当内部类中定义了静态成员,该内部类必须是static的。
②当外部类中的静态方法访问内部类时,内部类也必须是static的。
7)内部类想调用外部类的成员,需要使用:外部类名.this.成员,即OutterClassName.this表示外部类的对象。
如果写this.成员= =成员,调用的还是内部类的成员(属性或方法)。
8)Timer 和 TimerTask:
继承TimerTask 重写run()方法,
再用Timer类中的schedule方法定时调用,就能自动启用run()(不像以前似的要用 .XXX 调用)。
eg:内部类
class Xoo{
Timer timer = new Timer();
public void start(){
timer.schedule(new MyTask(), 0, 1000); //0表示立即开始,无延迟
timer.schedule(new StopTask(), 1000*10);//在10秒以后执行一次
}
class StopTask extends TimerTask{
public void run() {
timer.cancel();
}//取消timer上的任务
}
class MyTask extends TimerTask {
int i=10;
public void run() {
System.out.println(i--);
}
}
3.17匿名类
匿名内部类==匿名类
1)匿名内部类的格式:
new 父类或者接口(){
定义子类的内容
};
如下就叫匿名内部类!是继承于Uoo类的子类或实现Uoo接口的子类,并且同时创建了子类型实例,其中{}是子类的类体,可以写类体中的成员。
new Uoo(){
}
2)定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3)匿名内部类没有类名,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。可以理解为带内容的对象。
4)在匿名内部类中只能访问final局部变量。
5)匿名内部类中定义的方法最好不要超过3个。
eg1:匿名内部类的创建
public static void main(String[] args) {
Uoo u = new Uoo(); //创建Uoo实例 Uoo u1 = new Uoo(){};//创建匿名内部类实例
Uoo u2 = new Uoo(){
public void test() {//方法的重写
System.out.println("u2.test()");
}
};
u2.test();//调用在匿名内部类中重写的方法。
// new Doo();编译错误,不能创建接口实例
Doo doo = new Doo(){//实现接口,创建匿名内部类实例
public void test() {//实现接口中声明的抽象方法
System.out.println("实现test"); }
};
doo.test();//调用方法
}
}
interface Doo{
void test();
}
class Uoo{
public void test(){
}
}
eg2:匿名内部类中只能访问final局部变量
final Timer timer=new Timer();
timer.schedule(new TimerTask(){
public void run(){
timer.cancel();//在匿名内部类中只能访问final局部变量
}
}, 1000*10);
6)nonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以implements(实现)interface(接口)?
匿名内部类是可以继承其它类,同样也可以去实现接口的,用法为:
答案: 这样的用法在swing编程中是经常使用的,就是因为它需要用到注册监听器机制,而该监听类如果只服务于一个组件,
那么将该类设置成内部类/匿名类是最方便的。
3.18二维数组和对象数组
二维数组(假二维数组),Java中没有真正的二维数组!Java二维数组是元素为数组的数组。
对象数组:元素是对象(元素是对象的引用)的数组。
Point[] ary;// 声明了数组变量ary
ary = new Point[3];// 创建了数组对象
// new Point[3]实际情况:{null,null,null}
// 数组元素自动初始化为null,并不创建元素对象!
System.out.println(ary[1]);// null
ary[0] = new Point(3, 4);
ary[1] = new Point(5, 6);
ary[2] = new Point(1, 2);
System.out.println(ary[1]);// 默认调用了对象的toString()
System.out.println(ary[1].toString());//结果上面的一样
//toString是Object类定义,子类继承的方法
//在输出打印对象的时候,会默认调用,重写这个方法可以打印的更好看!
System.out.println(Arrays.toString(ary));//输出3个对象
System.out.println(ary.toString());//地址值
int[] c={1,3,5,7};
System.out.println(c[1]);
//System.out.println(c[1].toString());//错误,不能在int类型调用toString()
3.19其他注意事项
1)Java 文件规则:
一个Java源文件中可以有多个类,但只能有一个公有类!其他类只能是默认类(包中类)而且Java的文件夹一定与公有类类名一致!
如果没有公有类,可以和任何一个文件名一致。
一般建议:一个文件一个公有类!一般不在一个文件中写多个类
2)JVM内存结构堆、栈和方法区分别存储的内容:
JVM会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存储使用new关键字创建的对象。
栈用于存放程序运行过程当中所有的局部变量。
一个运行的Java程序从开始到结束会有多次方法的调用。
JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。
一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。当某一个方法调用完成后,其对应的栈帧将被清除。
方法区该空间用于存放类的信息。Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。
类的各种信息都在方法区保存。