引用类型变量java
为了可以对实例化的对象进行访问控制,需一个特殊的变量,即引用。对引用有两点须要说明: 1. 引用类型变量能够存储该类对象的地址信息,一般称为“指向该类的对象”,当一个引用类型变量指向该类的对象,就能够经过这个变量对对象实施访问。 2. 除8种基本类型以外,用类、接口、数组等声明的变量都称为引用类型变量,简称“引用”。
null和NullPointerException程序员
须要注意:当一个引用的值为null的时候,若是经过引用访问对象成员变量或者调用方法是不合逻辑的(因其没有指向某对 象,天然不会有属性和方法)。此时,会产生NullPointerException(空指针异常)。
方法的重载数组
方法的签名安全
方法的签名包含以下两个方面:方法名和参数列表。 Java语法规定,一个类中不能够有两个方法签名彻底相同的方法,即:一个类中不能够有两个方法的方法 名和参数列表都彻底相同,可是,若是一个类的两个方法只是方法名相同而参数列表不一样,是能够的。
方法重载及其意义数据结构
在Java语言中,容许多个方法的名称相同,但参数列表不一样,此种方式称为方法的重载(overload)。函数
构造方法this
构造方法语法结构操作系统
构造方法是在类中定义的方法, 但不一样于其余的方法,构造方法的定义有以下两点规则:线程
经过构造方法初始化成员变量指针
this关键字的使用
this指代当前对象
默认的构造方法
JAVA语法规定,任何一个类都必须含有构造方法,假如源程序中没有定义,则编译器在编译时将为其添加 一个无参的空构造方法(此方法称之为“默认的构造方法”)
构造方法的重载
不少时候,为了使用的方便,能够对一个类定义多个构造方法,这些构造方法都有相同的名称(类名),只是方法的参数不一样,称之为构造方法的重载。 在建立对象时,Java编译器会根据不一样的参数调用来不一样构造方法
引用类型数组
数组是对象
在java中,数组属于引用数据类型,数组对象存放在堆中存储,数组变量属于引用类型,存储数组对象的地址信息,指向数组对象。 而数组的元素能够当作数组对象的成员变量(只不过类型所有相同)
引用类型数组的声明
刚刚声明的数组为基本类型数组,除了基本类型数组之外,也能够声明引用类型数组。所谓引用类型数组,即数组元素的类型不是基本类型(int,char,float。。) , 而是引用类型,看以下代码:
Cell [ ] cells = new Cell [ 4 ] ;
其内存分配以下图
从上图示能够看出,new Cell[4]实际是分配了4个空间用于存放4个Cell类型的引用,并赋初始值为null,而并不是是分配了4个Cell类型的对象。
引用类型数组的初始化
若是但愿每个元素都指向具体的对象,则须要针对每个数组元素进行“new”运算。与基本类型数组同样,也能够采用静态初始化的方式进行初始化操做。以下代码所示:
Cell[ ] cells = new Cell[4]; cells[0] = new Cell(0,4); cells[1] = new Cell(1,3); cells[2] = new Cell(1,4); cells[3] = new Cell(1,5);
等价于:
Cell[ ] cells = new Cell[ ] { new Cell(0,4) , new Cell(1,3) , new Cell(1,4) , new Cell(1,5) } ;
如上数组内存分配图以下图– 6 所示:
数组的类型是基本类型数组
int [ ][ ] arr = new int[3][ ]; arr[0] = new int[2]; arr[1] = new int[3]; arr[2] = new int[2]; arr[1][1] = 100; 分析如上代码能够看出,变量arr指向一个数组,该数组有三个元素,每一个元素都是int类型数组,长度分别为 2,3,2,arr[1][1]=100表示将arr数组中的第2个元素(数组)的第2个元素赋值为100, 其内存分配如图所示:
对象内存管理
堆内存
对象存储在堆中
JVM在其内存空间开辟了一个称为“堆”的存储空间,这部分空间用于存储使用new关键字所建立的对象。请看以下代码:
Cell c = new Cell ();
其内存分布如图所示:
从图中能够看到右侧的堆内存,new Cell()所建立的对象在堆中分配,同时成员变量亦在此分配,并赋初始值为零。引用类型变量c在栈内存中分配,其中保存的数据,为对象在堆内存中的地址信息,假设对象在堆内存的地址为40DF,则c中保存的便是40DF。
成员变量的生命周期
当声明好对象以后,对该对象(堆中的Cell)的访问须要依靠引用变量(栈中的c),那么当一个对象没有任 何引用时,该对象被视为废弃的对象,属于被回收的范围,同时该对象中的全部成员变量也随之被回收。 能够这样认为,成员变量的生命周期为:从对象在堆中建立开始到对象从堆中被回收结束。 Cell c = new Cell(); c = null ; 当将c赋值为null时,表示c再也不指向刚刚分配的对象空间,此时成员变量失效。
垃圾回收机制
Java程序的内存泄漏问题
System.gc()方法
非堆---栈
栈用于存放方法中的局部变量
JVM在其内存空间开辟一个称为”栈”的存储空间,这部分空间用于存储程序运行时在方法中声明的全部的局部变量,例如,在main方法中有以下代码:
Cell c = new Cell ( ); int num = 5;
其内存分配如图所示:
说明:方法中的变量即为局部变量,是在栈内存中分配,若变量为值类型,则在栈中存储的就是该变量的值。若变量为引用类型,则在栈中存储的是堆中对象的地址。
局部变量的生命周期
成员变量和局部变量
成员变量与局部变量的差异以下: 局部变量: 1) 定义在方法中; 2) 没有默认值,必须自行设定初始值; 3) 方法被调用时,存在栈中,方法调用结束时局部变量从栈中清除; 成员变量: 1) 定义在类中,方法外; 2) 由系统设定默认初始值,能够不显式初始化; 3) 所在类被实例化后,存在堆中,对象被回收时,成员变量失效;
非堆---方法区
方法区用于存放类的信息
方法区用于存放类的信息,Java程序运行时,首先会经过类装载器载入类文件的字节码信息,通过解析后将其装入方法区。类的各类信息(包括方法)都在方法区存储,看以下代码:
Cell c = new Cell();
程序在执行这句话时,Cell类首先被装载到JVM的方法区,其中包括类的基本信息和方法定义等,以下图– 3 所示:
经过图示能够看出,在方法区中,包含Cell类的字节码文件,及类的基本信息及方法drop等。
方法只有一份
当类的信息被加载到方法区时,除了类的类型信息之外,同时类内的方法定义也被加载到方法区;
类在实例化对象时,多个对象会拥有各自在堆中的空间,但全部实例对象是共用在方法区中的一份方法定义的。意味着,方法只有一份。看以下代码:
JFrame f1 = new JFrame(); JFrame f2 = new JFrame(); f1.setSize(200, 300); f2.setSize(300,400);
如上的代码中,对象有两个,可是setSize方法只有一份,分别针对f1指向的对象和f2指向的对象调用了两次。
继承
泛化的过程
extends关键字
继承中构造方法
父类引用指向子类的对象
一个子类的对象能够向上造型为父类的类型。
重写
方法的重写
重写中使用super关键字
在子类重写的方法中,能够经过super关键字调用父类的版本
重写和重载的区别
重载与重写是彻底不一样的语法现象,区别以下所示: · 重载: 是指在一个类中定义多个方法名相同但参数列表不一样的方法,在编译时,根据参数的个数和类型来决定绑定哪一个方法。 · 重写: 是指在子类中定义和父类彻底相同的方法,在程序运行时,根据对象的类型(而不是引用类型)而调用不一样的方法。 分析以下代码的输出结果: class Super { public void f() { System.out.println ("super.f()"); } } class Sub extends Super { public void f() { System.out.println ("sub.f()"); } } class Goo { public void g(Super obj) { System.out.println ("g(Super)"); obj.f(); } public void g(Sub obj) { System.out.println ("g(Sub) "); obj.f(); } } class Test{ public static void main(String[] args){ Super obj = new Sub(); Goo goo = new Goo(); goo.g(obj); } } 首先,重载遵循所谓“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪一个方法, 由于变量 obj为Super类型引用, 因此,Goo的g(Super)被调用,先输出g(Super)。 重写遵循所谓“运行期绑定”,即在运行的时候,根据引用变量所指向的实际对象的类型来调用方法,由于 obj实际指向的是子类Sub的对象,所以,子类重写后的f方法被调用,即sub.f()。
包的概念
package语句
在Java语言中,命名冲突问题是用包(package)的概念来解决的,也就是说,在定义一个类时,除了定义类的名称通常还要指定一个包的名称,定义包名的语法以下所示:
package 包名;
须要注意的是,在定义包时,package语句必须写在Java源文件的最开始处,即在类定义以前,以下面的语句将为Point类指定包名为“test”:
package test; class Point{ …… }
在命名包名时,包名能够有层次结构,在一个包中能够包含另一个包
import语句
能够经过import语句对类的全称进行声明,import语句的语法以下所示:
import 类的全局限定名(即包名+类名);
有时,在import语句中也可使用“*”符号,例如:
import org.whatisjava.core.*;
访问控制修饰符
封装的意义
假设有水果店卖水果,分两种方式进行管理,方式一为须要店员,由店员实现取水果、包装、找零等功 能。方式二为不须要店员,由顾客自行完成取水果、包装、找零等功能。 那么想想,哪种方式更适合管理呢?通常认为方式一更适合,由于方式二没有人来进行管理,安全性 较低,除非来的都是活雷锋,彻底靠自觉。而方式一的安全性更高一些,并不是任何人均可以操做水果。 在软件系统中,经常经过封装来解决上面的问题。即:将容易变化的、具体的实现细节(卖水果)封装起 来,外界不可访问,而对外提供可调用的、稳定的功能(店员),这样的意义在于: 1. 下降代码出错的可能性,更便于维护。 2. 当内部实现细节改变时,只要保证对外的功能定义不变,其余的模块不须要更改。 在软件系统中,封装经常须要依靠一些访问控制修饰符来实现。
访问控制修饰符修饰类
访问控制符修饰成员
4种访问修饰(public、private、protected、默认),均可以修饰成员,其权限以下图所示
public 修饰符,在任何地方均可以访问;protected能够在本类、同一包中的类、子类中访问,除此以外的其它类不能够访问;默认方式为能够本类及同一包中的类访问,除此以外其它类不能够访问;private只能够在本类中访问,其它任何类都不能够。
static关键字
static修饰成员变量
static修饰方法
static块
static块为属于类的代码块,在类加载期间执行的代码块,只执行一次,能够用来在软件中加载静态资源(图像、音频等等)。
final关键字
final修饰变量
final关键字修饰变量,意为不可改变。final能够修饰成员变量,也能够修饰局部变量,当final修饰成员变量时,能够有两种初始化方式:
final关键字修饰局部变量,在使用以前初始化便可。
final修饰方法
final关键字修饰的方法不能够被重写。使一个方法不能被重写的意义在于:防止子类在定义新方法时形成的“不经意”重写。
final修饰类
final关键字修饰的类不能够被继承。使一个类不能被继承的意义在于:能够保护类不被继承修改,能够控制滥用继承对系统形成的危害。在JDK中的一些基础类库被定义为final的,例如:String、Math、Integer、Double 等等。
static final常量
static final 修饰的成员变量称为常量,必须声明同时初始化,而且不可被改变。常量建议全部字母大写。
实际应用中应用率较广,由于static final常量是在编译期被替换的,能够节约没必要要的开支,以下代码演示了static final的用法:
class Foo { public static final int NUM = 100; } class Goo { public static void main(String[] args) { Sytem.out.println(Foo.NUM); // 代码编译时,会替换为:System.out.println(100); } }
说明:static final常量Foo.NUM会在编译时被替换为其常量值(100),在运行Goo类时,Foo类不须要被载入。这样减小了没必要要的开支。
使用抽象类
抽象方法和抽象类
抽象类不能够实例化
继承抽象类
抽象类的意义
定义抽象类的意义在于: 1. 为其子类提供一个公共的类型(父类引用指向子类对象); 2. 封装子类中的重复内容(成员变量和方法); 3. 定义有抽象方法,子类虽然有不一样的实现,但该方法的定义是一致的。(子类须要实现此抽象方法)。
使用接口
定义一个接口
接口能够当作是特殊的抽象类。即只包含抽象方法和常量的抽象类。能够经过interface关键字来定义接口。看以下代码:
interface Runner { public static int DEFAULT_SPEED = 100 public void run(); }
注意,run()方法,此处能够省略public abstract。因其默认就是public abstract的。
实现接口
接口的继承
接口间能够存在继承关系,一个接口能够经过extends关键字继承另一个接口。子接口继承了父接口中定义的全部方法
接口和抽象类的区别
多态
多态的意义
// 前面所讲解的现象就是多态,多态即多种形态,主要有两个方面的表现。 // 首先,一个类型的引用在指向不一样的对象时会有不一样的实现,看以下的代码: 达内职员 emp1 = new 达内讲师(); 达内职员 emp2 = new 达内项目经理(); emp1.完成工做(); emp2.完成工做(); // 一样是达内职员类型,当指向不一样的对象时,能够有不一样的表现。 // 其次,一样一个对象,造型成不一样的类型时,会有不一样的功能,看以下代码所示: 达内讲师 teacher = new 达内讲师(); 企业技术顾问 consultant = teacher; 技术图书做者 author = teacher; consultant.培训员工(); author.编辑稿件(); // 经过上面的代码,能够看出,一样的达内讲师对象,当将其造型为企业技术顾问及技术图书做者时,能够实现不一样的功能。
向上造型
父类的引用指向子类的对象。这个现象就是下面要给你们介绍的现象,叫作向上造型。
一个类的对象能够向上造型的类型有:父类的类型及其实现的接口类型。当发生向上造型时,Java编译器会根据类型检查调用方法是否匹配
强制转型
在实际应用中,还能够经过强制转换将父类型变量转换为子类型变量,前提是该变量指向的对象确实是该子类类型。也可经过强制转换将变量转换为某种接口类型,前提是该变量指向的对象确实实现了该接口。若是在强制转换过程当中出现违背上述两个前提,将会抛出ClassCastException。
instanceof关键字
在强制转型中,为了不出现ClassCastException,能够经过instanceof关键字判断某个引用指向的对象是否为指定类型。
内部类
定义成员内部类
一个类能够定义在另一个类的内部,定义在类内部的类称之为Inner,其所在的类称之为Outer;Inter 定义在Outer的内部,一般只服务于Outer,对外不具有可见性,Inter能够直接调用Outer的成员及方法(包括私有的)。
建立内部类对象
通常状况下,Inner对象会在Outer对象中建立(构造方法或其余方法);Inner对象中会有一个隐式的引用指向建立它的Outer类对象。
定义匿名内部类
若是在一段程序中须要建立一个类的对象(一般这个类须要实现某个接口或者继承某个类),并且对象建立后,这个类的价值也就不存在了,这个类能够没必要命名,称之为匿名内部类。
面向对象三大特征:封装、继承、多态