目录
一、类和对象
- 对象:软件中存在的单个个体
- 类:对象的模板,根据对象的属性抽取类的数据结构
1.定义类
(1)定义类的成员变量
- 类的定义包括"成员变量"的定义和"方法"的定义,其中"成员变量"用于描述该类型对象的共同的数据结构.
- 对象创建后,其成员变量可以按照默认的方法初始化
- 初始化对象成员变量时,其默认值八大基本类型为0,boolean为false,引用类型为null
(2)定义类的方法
类中除了定义成员变量,还可以定义方法,用于描述对象的行为,封装对象的功能.
(3)创建并使用
- 使用new关键字创建对象
- 类定义完成后,可以使用new关键字创建对象.创建对象的过程通常称为实例化.
- 引用类型变量
为了能够对实例化的对象进行访问控制,需要使用一个特殊的变量--引用.
-除8种基本类型之外,用类\接口\数组等声明的变量都称为应用类型变量,简称"引用" .
- 访问对象的成员变量\调用方法
可以通过引用访问对象的成员变量或调用方法.
- 方法的签名
方法的签名包括:方法名和参数列表
一个类中,不能有两个方法的签名完全相同,如果一个类的两个方法只是方法名相同而参数不同是可以的.
(4)方法重载及其意义
在java语言中,允许多个方法的名称相同,但参数列表不同,称为方法的重载
编译器在编译时会根据签名来绑定调用不同的方法
二、构造方法
1.构造方法的语法结构
- 构造方法是在类中定义的方法,不同于其他的方法,构造方法的定义有两点规则:
-构造方法的名称必须与类名相同.
-构造方法没有返回值,但也不能写void.
2.通过构造方法初始化成员变量
构造方法常用于实现对象成员变量的初始化.
创建对象时,构造方法写在new关键字后,可以理解为:"new"创建了对象,而构造方法对该对象进行初始化.
3.this关键字的使用
- this关键字用在方法体中,用于指向调用该方法的当前对象;简单的说:那个对象调用方法,this指的就是哪个对象.严格来讲在方法中需要通过this关键字指明当前对象.
- this只能用在方法中,访问成员变量之前默认有个this.
- 在构造方法中,用来初始化成员变量的参数一般和成员变量取相同的名字,这样会有利于代码的可读性,但此处就必须通过this关键字来区分成员变量和参数了(不能省略this)
4.默认的构造方法
任何一个类都必须含有构造方法
如果源程序中没有定义,编译器在编译时将为其添加一个无参的空构造方法(称为"默认的构造方法").
当定义了构造方法后,java编译器将不再添加默认的构造方法
5.构造方法的重载
为了使用方便,可以对一个类定义多个构造方法,这些构造方法都有相同的名称(类名),方法的参数不同,称为构造方法的重载.
在创建对象时,java编译器会根据不同的参数调用不同的构造方法.
一个构造方法可以通过this关键字调用另一个重载的构造方法:
public Student(String name,int age){
this.name=name;
this.age=age;
}
public Student(){
this("无名氏",1);
}
6.引用类型数组
引用类型数组的声明
数组的元素可以是任何类型,当然也包含引用类型.
如:Airplane[] as=new Airplane[4];
引用类型数组的初始化
引用类型数组的默认初始值都是null.
如果希望每一个元素都指向具体的对象,需要针对每一个数组元素进行"new"运算.
继承
7.extends关键字
通过extends关键字可以实现类的继承;
派生类(Sub class)可以继承超类(Super class)的成员变量和成员方法;
java语言不支持多重继承,一个类只能继承一个超类,但一个超类可以有多个派生类.
继承中构造方法
派生类的构造方法中必须通过super关键字调用超类的构造方法.
如果派生类的构造方法中没有调用超类的构造方法,java编译器会自动的加入对超类无参构造方法的调用(如果该超类没有无参的构造方法,会有编译错误).
超类的引用指向派生类的对象
一个派生类的对象可以向上造型为超类的类型.即,定义超类型的引用可以指向派生类的对象.
父类的引用可以指向子类的对象,但通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分.
8.重写
方法的重写
子类可以重写(覆盖)继承自父类的方法,即方法名和参数列表与父类的方法相同;但方法的实现不同.
当子类对象的重写方法被调用时(无论是通过子类的引用调用还是通过父类的引用调用),运行的是子类的重写后的版本.
重写中使用super关键字
子类在重写父类的方法时,可以通过super关键字调用父类的版本.
class Foo{
public void f(){
System.out.println("Foo.f()");
}
}
class Goo extends Foo{
public void f(){
super.f();
System.out.println("Goo.f()");
}
}
Foo obj2=new Goo();
obj2.f();
输出的结果为;
Foo.f();
Goo.f();
子类重写方法中的super.f();调用了父类的版本;这样的语法通常用于子类的重写方法在父类方法的基础上进行行为功能的扩展.
9.重写和重载的区别
重载与重写是完全不同的语法现象,区别如下:
-重载是指在一个类中定义多个方法相同但参数列表不同的方法,在编译时,根据参数的个数和类型来决定绑定哪个方法.
-重写是指在子类中定义和父类完全相同的方法,在程序运行时,根据对象的类型不同(而不是引用类型)而调用不同的版本.
10.package语句
定义类时需要指定类的名称,但如果仅仅将类名作为类的唯一标识,则不可避免的出现命名冲突的问题,这会给组件复用以及团队间的合作造成很大的麻烦 !
在java语言中,用包(package)的概念来解决命名冲突的问题.在定义一个类时,除了定义类的名称一般还要指定一个包名,定义包名的语法为:package包名;
包名写在java原文件的最开始,在类定义之前
包名也可以有层次结构,在一个包中可以包含另一个包. package 包名1包名2...包名n
如果各个公司或开发组织的程序员都随心所欲的命名包名的话,仍然不能从根本上解决命名冲突的问题.因此,在指定包名的时候应该按照一定的规范,
如:org.apache.commons.lang.StringUtils
StringUtils是类名而org.apache.commons.lang是多层包名其含义如下:org.apache表示公司或组织的信息(是这个公司(或组织)域名的反写;commons表示项目的名称信息;lang表示模块的名称信息)
11.import语句
访问一个类时需要使用该类的全称,但这样的书写方式过于繁琐; org.whatisjava.core.Point p=new ore.whatisjava.core.Point();
可以通过import语句对类的全称进行声明.import语句的语法为:import类的全局限定名(即包名+类名);import org.whatisjava.core.Point;
通过import语句声明了类的全称后,该原文件中就可以直接使用类名来访问了.
import org.whatisjava.core.Point;
public class Main{
public static void main(String[] args){
Point p=new Point(100,200);
}
}
12.访问控制修饰符
public和private
private修饰的成员变量和方法仅仅只能在本类中调用;
public修饰的成员变量和方法可以在人任何地方调用.
public修饰的内容是对外提供可以被调用的功能,需要相对稳定;
private修饰的内容是对内实现的封装,如果"公开"会增加维护的成本.
protected和默认访问控制
用protected修饰的成员变量和方法可以被子类及同一个包中的类使用.
默认访问控制即不书写任何访问控制符.默认访问控制的成员变量和方法可以被同一个包中的类调用.
访问控制符修饰类
对于类的修饰可以使用public和默认方式.public修饰的类可以被任何一个类使用;默认访问控制的类只可以被同一个包中的类使用.
protected和private可以用于修饰内部类.
13.final关键字
final修饰变量
final关键字修饰变量,意为不可改变.
final修饰成员变量,两种方式初始化:
-声明同时初始化
-构造函数中初始化
final关键字也可以修饰局部变量,使用之前初始化即可.
final修饰方法
final关键字修饰的方法不可以被重写.
使一个方法不能被重写的意义在于:防止子类在定义新方法时造成的"不经意"重写.
final修饰类
final关键字修饰的类不可以被继承.
final class Foo{}
class Goo extends Foo{} //会有编译错误
JDK中的一些基础类库被定义为final的,例如:String\Math\Integer\Double等待.
使一个类不能被继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害.
static关键字
static修饰成员变量
用static修饰的成员变量不属于对象的数据结构;
static变量是属于类的变量,通常可以通过类名来引用static成员;
static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static成员变量只有"一份",无论该类创建了多少对象.
static修饰方法
通常的方法都会涉及到具体对象的操作,这些方法在调用时,需要隐式的传递对象的引用(this).
由于static在调用时没有具体的对象,因此在static方法中不能对非static成员(对象成员)进行访问.
static
static块:属于类的代码块,在类加载期间执行的代码块,只执行一次,可以用来在软件中加载静态资源.
class Foo{
static{
//类加载期间,只执行一次
System.out.println("Load Foo.class");
}
public Foo(){
System.out.println("Foo()");
}
}
Foo foo=new Foo();
输出的结果为:
Load Foo.class
Foo();
类加载时,运行静态块,在创建对象之前.
static final常量
static final修饰的成员变量称为常量,必须声明同时初始化,不可被改变.
static final常量会在编译其被替换,例如:
class Foo{
public static final int NUM=100;
}
class Foo{
public static final int NUM=100;
}
class Goo{
public static void main(String[] args){
System.out.println(Foo.NUM);
//代码编译时,会替换为:System.out.println(100);
}
}
static final常量Foo.NUM会在编译时被替换为其常量值(100),在运行Goo类时,Foo类不需要被载入.
内部类
定义成员内部类
一个类可以定义在另一类的内部,定义在类内部的类称为Inner,其所在的类称为Outer;
Inner定义在Outer的内部.通常只服务于Outer,对外部不具备可见性,Inner可以直接调用Outer的成员及方法(包括私有的).
创建内部类对象
一般情况下,Inner对象会在Outer对象中创建(构造方法或其他方法);Inner对象中会有一个隐式的引用指向创建它的Outer类对象.
定义匿名内部类
如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且对象创建后.这个类的价值也就不存在了,这个类可以不必命名,称为匿名内部类.
抽象方法不完整不写大括号,抽象类也不完整,不能被new对象
sbstract class FlyingObject{//不完整
width,height,x,y
abstract void step();//不完整
}
类名也是一种数据类型可以定义声明变量的. new FlyingObject();不行 FlyingObject f;可以 FlyingObject[] f;可以 new FlyingObject[5]可以创建FlyingObject数组对象只要是数据类型就能创建数组对象.
FlyingObject f=new FlyingObject();不可以 FlyingObject[] f=new FlyingObject[5];可以
1由sbstract修饰2包含抽象方法的类必须是抽象类3抽象类不能被实例化(new对象)4抽闲类单独存在没有意义是需要被继承的,有派生类4.1重写所有的抽象方法(变不完整为完整)4.2继承后也声明抽象类(一般不用)
三、抽象方法和抽象类
由abstract修饰的方法为抽象方法,抽象方法只有方法的定义,没有方法体实现,用一个分号结尾;
即使一个类中没有抽象方法,也可以将其定义为抽象类,同样,该类不可以实例化.
abstract和final关键字不可以同时用于修饰一个类,因为final关键字修饰的类不能继承,而abstract需要继承.
1.抽象类的意义在于:
1封装共有的属性和行为//代码复用2为所有派生类提供统一的类型//向上造型3可以包含抽象方法,为派生类提供统一的入口(能点出来)派生类的具体实现不同,但入口的是一致的.
为了造型,子类只能继承父类的不写点不出来
-为其子类提供一个公共的类型;
-封装子类中的重复内容(成员变量和方法);
-定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的.
2.使用接口
定义一个接口
接口可以看成是特殊的抽象类.即只包含有抽象方法的抽象类;
interface Runner{//通过interface关键字定义接口
public int DEEAULT_SPEED=100;//接口中不可以定义成员变量,但是可以定义常量.
public void run();//接口中只可以定义没有实现的方法(可以省略public abstract)
}
3.实现接口
与继承不同,一个类可以实现多个接口,实现的接口直接用逗号分隔.当然该类要实现这些接口中定义的所有方法;
一个类可以通过implements关键字"实现"接口.一个类实现了某个接口后必须实现该接口中定义的所有方法.
接口可以作为一种类型声明变量,一个接口类型的变量可以引用实现了该接口的类的对象;通过该变量可以调用该接口中定义的方法(具体的实现类提供了方法的实现).
Runner runner=new AmericanCurl();
4.接口的继承
接口间可以存着继承关系,一个接口可以通过extends关键字继承另一个接口.子接口继承了父接口中定义的所有方法.
5. 多态
多态的意义
一个类型的引用在指向不同的对象时会有不同的实现
同一个对象,造型成不同的类型时,会有不同的功能
向上造型
一个类的对象可以向上造型的类型有:
-超类的类型
-其实现的接口类型
java编译器根据引用的类型检查调用方法是否匹配.
强制转型
可以通过强制转换将超类型变量转换为派生类的变量,前提是该变量指向的对象确实是该派生类类型.
也可以通过强制转换将变量转换为某种接口类型,前提是该变量指向的对象确实实现了该接口.
如果在强制转换过程中出现违背上述两个前提,将会抛出ClassCastException
instanceof关键字
在强制转型中,为了避免出现ClassCastException.可以通过instanceof关键字判断某个引用指向的对象是否为指定类型.
FlyingObject o=new Airplane();//通过instanceof运算判断o引用所指向的对象是否指定类型.
System.out.println(o instanceof Airplane);//true
Bee o1=null;//类型强制转换时最好配合instanceof判断,这样可以避免出现ClassCastException.
if(o instanceof Bee){
o1=(Bee)o;
}
对象内存管理
编译好的java程序需要运行在JVM中.
程序,无论代码还是数据,都需要存储在内存中.JVM为java程序提供并管理所需要的内存空间.
JVM内存分为"堆""栈"和"方法区"三个区域,分别用于存储不同的数据.
堆内存
对象存储在堆中
JVM在其内存空间开辟一个称为"堆"的存储空间;
这部分空间用于存储使用new关键字所创建的对象.
成员变量的生命周期
访问对象需要依靠引用变量.
当一个对象没有任何引用时,被视为废弃的对象,属于被回收的范围.该对象中的所有变量也随之被回收.
成员变量的生命周期为:从对象在堆中创建开始到对象从堆中被回收结束.
Airplane a=new Airplane();
a=null;
//不在指向刚分配的对象空间,成员变量失效
垃圾回收机制
垃圾回收器(Garbage Collection,GC)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用的对象.
java程序员不用担心内存管理,因为垃圾收集器会自动进行回收管理.
java程序的内存泄漏问题
内存泄漏是指,不再使用的内存没有被及时的回收.严重的内存泄漏会因过多的内存占用而导致程序的崩溃.
GC线程判断对象是否可以回收的依据是该对象是否有引用指向,因此,当确定该对象不再使用时,应该及时将其引用设置为null.
System.gc()方法
GC的回收对程序员来说是透明的,并不一定一发现有无引用的对象,就立即回收.
一般情况下,当我们需要GC线程即可回收无用对象时,可以调用System.gc()方法.
System.gc()用于建议虚拟机马上调度GC线程回收资源,具体的实现策略取决于不同的JVM系统.
四、非堆-栈
栈用于存放方法中的局部变量
JVM在其内存空间开辟一个称为"栈"的存储空间;
这部分空间用于存储程序运行时在方法中声明的所有局部变量.
局部变量的生命周期
一个运行的Java程序从开始到结束会有多次方法的调用。JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。
一个栈帧对应一个正在调用的方法,栈帧中存储了该方法的参数、局部变量等数据。当某一个方法的调用完成后,其对应的栈帧将被清除,局部变量失效。
成员变量和局部变量
成员变量和局部变量的差别如下:
局部变量:
-定义在方法中;
-没有默认值,必须自行设定初始值;
-方法被调用时,存在栈中,方法调用结束,从栈中清除;
成员变量:
-定义在类中,方法外;
-有默认初始值,可以不显示初始化;
-所在类被实例化后,存在堆中,对象被回收时,成员变量失效;
非堆-方法区
方法区用于存放类的信息;
方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储。
方法只有一份
当类的信息被加载到方法区时,除了类的类型信息以外,同时类内的方法定义也被加载到方法区;
类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。