6.1 包装类
6.1.1 基本类型对应的包装类
基本数据类型 | 对应的包装类 |
byte | Byte |
short | Short |
int | Integer |
float | Float |
double | Double |
char | Character |
boolea | Boolean |
6.1.2 自动装箱与自动拆箱
(1)定义:自动装箱:可以把一个基本类型遍历直接赋值给对应的包装类变量或object类
自动拆箱:允许直接把包装类对象直接赋给一个对应的基本类型变量
- 例子:
Integer inObj = 5; //直接把一个基本类型变量赋值给Integer对象(自动装箱)
Object boolObj = true; //直接把一个boolean类型变量赋值给Object变量(自动装箱)
int it = inObj; //直接把一个Integer对象赋值给int类型变量(自动拆箱)
6.2.3 字符串与基本类型之间的转换
①String类型 转为 基本类型
String strInt = “123”;
String strFlo = “4.31”;
//把一个特定字符串转换成int变量
int a = Integer.parseInt( strInt );
int b = new Integer( strInt );
②String类型 转为 包装类型
//String 类型转为 Integer 包装类
Integer i = Integer.valueOf( “6543” ) ;
③基本类型 转为String类型
//把一个特定的字符串转换为float变量
Float float1 = Float.parseFloat( strFlo );
Float float2 = new Float(strFlo );
//float变量转换为String变量
String floStr = String.valueOf( 2.414f );
//double变量转换为String变量
double douStr = String.valueOf( 6.232 );
//boolean变量转换为String变量
boolean booStr = String.valueOf( true );
Ps: Integer integer1 = 150;
Integer integer2 = 150;
integer1 == integer2 -----> false -----> 因为 缓存数组:-128~127之间的整型数
6.2 == 和 equals
Integer i1 = 2; 创建了一个整型对象
Integer i2 = Integer.valueOf(2); 不创建新的对象,i2和i1引用同一个对象
Integer i3 = Integer.valueOf(2); 不创建新的对象,i3和i1引用同一个对象
Integer i4 = Integer.valueOf("2"); 创建了一个整型对象
Integer i5 = Integer.valueOf("2"); 创建了一个整型对象
根据对象的创建过程,很明了,程序运行结果为:
test ==
i1 == i2 ? true
i1 == i4 ? false
i2 == i3 ? true
i2 == i4 ? false
i4 == i5 ? false
test equals
i1.equals(i2) ? true
i1.equals(i4) ? true
i2.equals(i3) ? true
i2.equals(i4) ? true
i4.equals(i5) ? true
6.2.1 基本数据类型
①== 比较内容
②equals 比较内容
6.2.2 引用类型
①== 比较地址
Ps:必须要同一类型 或者两者必须存在继承关系,否则编译不通过
②equals 比较内容
例://实现自己的equals,比较引用类型的内容
public class EqualTest{
public static void main( String[] args ){
String s1 = "A";
String s2 = new String( "A" );
//equals 不是 String 自己的方法, 它也是 Object 类里面定义方法
//只是 String 对它进行了复写
System.out.println( s1.equals( "A" ) ); //true
System.out.println( s1.equals( s2 ) ); //true
Person p1 = new Person( "Limin", 25 );
//Person p2 = p1;
//Person p2 = new Person( "Limin", 25 );
System.out.println( "[015]"+ p2.equals( p1 ) );
}
}
//为了给我自己定义的类, 做一个等值判断的标准方法
class Person{
String name;
int age;
public Person( String _name, int _age ){
name = _name;
age = _age;
}
//只要名字一样, 年龄一样, 我就认为他是同一个人
//public boolean equals( Object o )
public boolean equals( Object o ){
if( o==this )
return true; //因为两个对象引用地址一样
if( o!=null ){
//instanceof:
if( o.getClass()==Person.class ){
Person p = (Person)o;
if( this.name!=null && p.name!=null
&& this.name.equals(p.name)
&& this.age == p.age ){
return true;
}
}
}
return false;
}
}
6.3 常量池
6.4 final修饰符
6.4.1 final修饰成员变量
- 程序员必须给成员变量做显式初始化,初始化后不可改变值
(2)初始化位置:
[1]非静态成员变量:
①声明时初始化
②初始化块中初始化
③构造器中初始化
[2]静态成员变量(类变量):
①声明时初始化
②静态初始化块中初始化
6.4.2 final修饰局部变量
- 声明时可以初始化或不初始化,要使用时必须初始化,初始化后不可改变值
- 初始化位置:
[1]方法体、语句块中的局部变量
①声明时初始化
②方法块中初始化
[2]形参列表中的局部变量
没初始化这一说,使用时也可以不初始化
6.4.3 final修饰基础类型和引用类型区别
(1)final修饰的基础类型表示值不可修改
(2)final修饰的引用类型表示引用的地址不可修改,但该引用中的非final修饰的属性和方法等是可以修改的
6.4.4 可执行宏替换的final变量
(1)定义:宏变量也称直接量(常量),已改全改。
(2)要求:①使用final修饰
②定义时初始化
③该初始值可以编译时就被确定下来
6.4.5 final修饰方法
(1)final修饰的方法不可被子类重写,但可以被继承
(2)子类重写父类非final的方法时,可以定义为final修饰的方法
6.4.6 final修饰类
- final修饰的类不可被继承,final类中的方法默认是final的
- 可以通过使用final修饰类的对象 配合 获取该类属性方法返回值中new该类对象,来实现该final修饰的类的属性或方法不被修改的效果,从而达到保护类中属性安全目的。
6.5 抽象类
6.5.1 抽象类的意义与特性
- 意义:
父类无法确认该方法具体的行为,从而让子类重写该抽象方法,添加其各自不同的具体行为,子类重写的方法不能再添加abstract。
- 特性:
①抽象类内可以没有抽象方法,但类内有抽象方法时,该类必须定义为抽象类
②如果没有实现完父类的抽象方法,那该类也必须定义为抽象类
③把多态用的灵活,通过模板来让子类实现
④abstract不能用来修饰任何的变量
⑤不可用private修饰任何抽象方法 (因为priavte会方法不可见,无法重写)
⑥不可用static修饰任何抽象方法
⑦static 与 abstract完全互斥吗 ------->可以应用到内部类中
6.5.2 抽象类定义格式
(修饰符) abstract class 名字{ }
Ps:只要类中有一个抽象方法,此类就被标记为抽象类
6.5.3 抽象类属性和方法定义格式
(1)常量、变量、方法、语句块的定义格式和普通类一样
(2)抽象方法的定义格式:
格式1: (修饰符) abstract 数据类型 名字();
格式2: (修饰符) abstract 数据类型 名字(参数列表);
6.5.4 抽象类内包含数据
抽象类里可以有:常量、变量、普通方法、抽象方法、语句块、对象
6.5.5 抽象类的调用与被调用
(1)抽象类调用普通类和接口:
①抽象类调用普通类和接口都跟普通类调用的一样。但是调用接口时,不一定要重写其 中的抽象方法。
(2)抽象类被调用:
①抽象类的调用只能通过继承extends来调用,从而获得抽象类里的特性,普通类名的 后面继承抽象类,抽象类除了被继承外没有任何意义,抽象类中抽象方法必须要被子类 重写。
如:public class 普通类名 extends 抽象类名{ }
②抽象类无法被接口调用,因为接口只能通过new来调用类,而抽象类只能通过继承 被调用,不能通过new被调用。
6.6 接口
6.6.1 定义格式
访问权限 interface 名字 { }
类实现接口 :类名 implements 要实现的接口名
(其中,类可以实现多个接口,中间用”,”号隔开)
6.6.2 接口的特性
[1]接口中的方法默认都是public,abstract修饰,属性默认都是static、final修饰
[2]接口中可以有 内部类、枚举、接口
[3]接口中不可以定义static方法和default方法,这两种方法都有方法体(JDK1.8以下)
[4]接口不可以实例化(即不可以new)
[5]定义接口可以用:default,public
6.6.3 接口中的数据
① 常量
② 抽象方法 (JDK1.8以上可以定义static方法和default方法 [有方法体])
③ 对象(内部类、枚举、接口等)
其中,修饰常量的修饰符可有省略。
6.6.4 抽象方法的定义格式
格式1 访问权限 abstract 数据类型 方法名();
格式2 访问权限 abstract 数据类型 方法名(参数);
其中,abstract可有省略。
6.6.5 匿名接口(也是匿名类)
(1)定义格式:
已有接口名 命名=new 已有接口名(){ 匿名接口 };
(2)匿名接口数据:
① 常量
② 抽象方法
③ 普通方法
④ 初始化块(不允许静态初始化块)
⑤ 对象
6.6.6 工厂模式
6.6.7 命令行模式
6.7 内部类
6.7.1 内部类的定义
在一个类的内部,定义一个类,这个类称为“内部类/嵌套类”
6.7.2 内部类作用
- 可以访问外部类中的成员,包括private成员
- 外部类可以创建内部类对象,通过该对象引用可以获取内部类中的成员
6.7.3 内部类的特性:
- 定义位置与个数:
内部类的定义位置只要在类内即可;内部类个数没有限制。
- 内部类编译:
内部类编译后,也会生成 .class 文件。
格式:[外部类名]$[内部类名].class
例:Outer$Inner.class
- 内部类的调用:
在一个类中,要使用另一个类的内部类,先要创建这个外部类的实例,再通过外部类的实例来创建内部类的实例,从而起到调用内部类的作用。
- 内部类的继承与实现:内部类可继承其他类或实现接口。
1)静态内部类可以有静态成员,而非静态内部类则不能有静态成员2)静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量3)非静态内部类的非静态成员可以访问外部类的非静态变量。
6.7.4 内部类可用的修饰符
- 权限修饰符:private 、(默认)、protected、public
- abstract 、final 都可以应用到内部类
- abstract 、static可以共用
6.7.5 内部类中可以拥有的成员
(1)非静态的内部类:不可以定义静态的成员
成员变量、行为方法、构造器、语句块(普通)、内部类
(2)静态内部类:可以定义静态的成员
6.7.6 访问外部类与内部类同名属性或方法
调用格式:外部类名.this.属性或者方法
例:调用外部类同名属性: Outer.this.score;
6.7.7 private修饰的内部类数据的调用
//private修饰的内部类的数据调用
public class InnerTest
{
public static void main( String[] args ){
Outer out = new Outer();
//错误: Outer.Inner可以在Outer中访问private
//Outer.Inner in = out.new Inner();
//限定不可以访问这一种类型: Outer.Inner
//Outer.Inner in = out.getInner();
A in = out.getInner( ); //将外部类实例.获取到的内部类引用赋给他实现的接口
String s = in.getName(); //通过内部类的实例获取到其private修饰的name属性
System.out.println( ""+ s );
}
}
//外部类
class Outer{
private String oName = "OName";
public void OutTest(){
}
//内部类,设置为 private
private class Inner implements A{
private String name = "";
public Inner( String name ){
this.name = name;
}
//实现接口中的getName()方法,返回name
public String getName(){
return name;
}
}
public Inner getInner(){
return new Inner( oName );
}
}
//A接口
interface A{
public String getName();
}
6.7.8 局部内部类(在方法体内的内部类)
- 实例化位置:
局部内部类只能在当前方法体中,创建该局部内部类之后实例化。
- 快捷定义方式:
[1]创建:
通过new一个类( ){ }; 创建一个对象,实际上它是创建一个匿名的内部类。
[2]调用:
①直接在后面加 .方法( ) 或 .属性
②用变量来保存该内部类的引用,通过引用来调用方法或属性。
( ps:该引用的所属类必须要存在要调用的成员。)
例1:
//匿名内部类
public class InnerTest
{
public static void main( String[] args ){
Outer out = new Outer();
out.OutTest();
}
}
//外部类
class Outer {
//方法OutTest()
public void OutTest(){
//通过 .方法() 来调用匿名内部类内的方法
new Object(){
private String name = "Hello";
public void getName(){
System.out.println( "[A] --> name: "+ name );
}
}.getName();
//用Object类型变量来保存匿名内部类的引用
Object o = new Object(){
private String name = "Hello";
public void getName(){
System.out.println( "[A] --> name: "+ name );
}
};
//错误: 找不到符号
o.getName(); //使用的是 Object 对象, 因为 Object 没有这个方法
}
}
例2:
//从内部类中访问局部变量i; 需要被声明为最终类型 final
public class InnerTest
{
public static void main( String[] args ){
Outer out = new Outer();
out.OutTest();
//out.test2();
}
}
//外部类
class Outer {
public A a;
//OutTest()方法
public void OutTest(){
int i = 5;
a = new A(){
public void test(){
//错误: 从内部类中访问局部变量i; 需要被声明为最终类型 final
System.out.println( "i: "+ i );
}
};
}
//test2()方法
public void test2(){
a.test();
}
}
//A类型,用于创建局部匿名内部类
class A {
public void test(){ }
6.7.9 类与接口的嵌套
一个对象嵌套在另一个对象里面,这个对象就叫嵌套型事物。对象内可以嵌套类(包括 普通类以及抽象类)或者接口
(1)class←嵌套→class
class | 类的定义与调用 | 数据的调用 |
静态内部类 | 定义:static class 静态内部类类名{ }; 调用:①外部类名.静态内部类类名 命名=new 外部类名.静态内部 类类名(); ②继承外部类,然后 静态内部类类名 命名=new 静态内部类类名(); | 命名.被调用数据; |
非静态内部类 | 定义:class 非静态内部类类名{ }; 调用:外部类名.非静态内部类类名 命名=new 外部类名().new 非静态内部类类名(); | 命名.被调用数据; |
(静态或非静态) 匿名内部类 | 定义:已有类类名 变量名=new 已有类类名(){ }; 调用:①外部类名 命名=new 外部类名(); (直接实例化外部类) ②已有类类名 命名=new 外部类名().变量名; | 无法调用数据 |
局部内部类 (不能是静态) | 定义:同 非静态内部类 (需要定义在方法体里) 调用:只能在方法体内调用: 局部内部类类名 命名=new 局部内部类类名(); | 命名.被调用数据 (只能在方法体内,定义的位置之后调用) |
局部匿名内部类(不能是静态) | 定义:同 匿名内部类 (需要定义在方法体里) 调用:外部类名 命名=new 外部类名(); 命名.所在方法名(); 或在定义后面加.调用数据 (直接调用所在方法即可) | 无法调用数据 |
(2)class←嵌套→interface
interface | 接口的定义与调用 | 数据调用 |
有名内部接口 (常量,抽象方法,对象) | 定义:interface 接口名{ }; 调用:①接口名.被调用数据 ②实现接口implements 接口名 后直接写 被调用数据 | ① ② |
匿名内部接口 (常量,变量,普通方法,普通语句块,对象) | 定义:已有接口名 命名=new 已有接口名(){ }; 调用:外部类名 命名=new 外部类名(); (跟匿名类一样) | 无法调用数据 |
(3)interface←嵌套→class
class | 类的定义与调用 | 数据调用 |
(静态或非静态) 内部类 | 定义:(static) class 静态或非静态内部类类名{ }; 调用:①实现或不实现接口都可以 内部类类名 命名=new (接口名.)内部类类名(); | 命名.被调用数据 |
(静态或非静态) 匿名内部类 | 定义:已有类类名 变量名=new 已有类类名(){ }; 调用:无法调用 | 无法调用数据 |
(4)interface←嵌套→interface
interface | 接口的定义与调用 | 数据调用 |
有名内部接口 (常量,抽象方法,对象) | 定义:interface 接口名{ }; 调用:①内部接口名.被调用数据 ②实现接口implements 内部接口名 后直接写 被调用数据 | ① ② |
匿名内部接口 (常量,变量,普通方法,普通语句块,对象) | 定义:已有接口名 命名=new 已有接口名(){ }; 调用:①外部接口名.命名 ②实现外部接口后,直接写 命名 | 无法调用数据 |
6.8 枚举类
6.8.1 枚举类入门
(1)枚举类历史:
java5 新增了enum关键词(它与class、interface关键词的地位相同)。用以定义枚举类。
(2)枚举类成员:
枚举类可以有自己的成员变量、方法、可以实现一个或者多个接口;也可以定义构造器(构造器必须定义为private,默认也是private)
- 与普通类的相同点:
一个java源文件中最多只能定义一个public访问权限的枚举类,且该java源文件也必须和该枚举类的类名相同。
(4)枚举类与普通类的区别:
[1]枚举类默认继承了java.lang.Enum类,而不是默认继承Object类。因此枚举类不能显示的继承其他父类。
[2]枚举类默认使用final修饰,因此枚举类不能派生子类,所以枚举值必须为抽象方法提供实现
[3]枚举类的所有实例必须在其第一行显示列出,否则这个枚举类永远不能产生实例。列出来的这些实例系统都自动添加public static final 修饰,无须程序员显示添加
- 作用:
使用枚举类可以使程序更加健壮,避免创建对象的随意性
6.8.2 例子
//测试类-----------------------------------------------------------------------
public class A13_EnumTest{
public static void main(String[] args){
//季节枚举实例
//SeasonEnum se = new SeasonEnum(); //枚举类型不能new
//[1]枚举类默认有一个values()方法,返回该枚举类的所有实例
for( SeasonEnum s : SeasonEnum.values()){
System.out.println( s ); //输出该枚举类的所有实例
}
//[2]使用枚举实例,通过EunmClass.varialbe形式来访问
SeasonEnum.SUMMER.judge(SeasonEnum.SUMMER);
//[3]枚举类中的ordinal()方法,用于获取枚举值在枚举类中的索引值(即声明的位置)
System.out.println("AUTUMN实例在SeasonEnum中的位置:" + SeasonEnum.AUTUMN.ordinal());
//[4]通过Enum的valueOf()方法获取指定枚举类的枚举值,从而获取该枚举值的属性或方法
Gender g1 = Enum.valueOf( Gender.class, "NO" );
Gender g2 = Enum.valueOf( Gender.class, "NOMAN" );
g1.name = "无";
g1.info();
g2.info();
}
}
//季节枚举类------------------------------------------------------------------
enum SeasonEnum{
SPRING, SUMMER, AUTUMN, WINTER;
public void judge( SeasonEnum s ){
switch( s ){
case SPRING:
System.out.println( "春天" );
break;
case SUMMER:
System.out.println( "夏天" );
break;
case AUTUMN:
System.out.println( "秋天" );
break;
case WINTER:
System.out.println( "冬天" );
break;
}
}
}
//性别枚举类-------------------------------------------------------------------
//[5]当开始使用枚举类的一个实例时,全部实例会自动实例化,由左到右
enum Gender implements GenderDesc{
NOMAN("不男"),
NO,
MAN("男"){ //为该实例添加自己的类体部分
public void info(){
System.out.println( "男士" );
}
},
WOMEN("女"){ //为该实例添加自己的类体部分
public void info(){
System.out.println( "女士" );
}
};
String name; //枚举属性name
//无参构造器(默认修饰符private)
Gender( ){
System.out.println("[无参构造器]:" + this );
}
//有参构造器
private Gender(String name){
this.name = name;
System.out.println("[有参构造器]:" + this.name);
}
//重写接口方法
public void info(){
System.out.println("[info()方法]:" + this.name);
}
}
//接口GenderDesc------------------------------------------------------------
interface GenderDesc{
void info();
}
6.9 类与接口之间的相互调用
调用方式 | 适用事物情况 |
implements 实现 | 类调用接口 |
extents 继承 | 类调用类,接口调用接口(可以连续继承多个) |
直接用 | 类调用类、接口调用接口、类调用接口、接口调用类、其他和上面2种一样 |
import 导入 | 用在不同包之间的事物,可以同时用上面3种方式。 |
6.9.1 class←→class
(1)直接用:
调用类:new 类名(); //调用并运行类
类名 变量名=new 类名(); //让变量的值=调用的类
调用类的特性,如:
//调用变量
用法1:变量类型 变量名=new 类名().要调用的变量;
用法2:类名 变量名1=new 类名();
变量类型 变量名2=变量名1.要调用的变量;
//调用方法
用法3: new 类名().要调用的方法名();
用法4: 类名 变量名=new 类名();
变量名.要调用的方法名();
//调用类的静态属性和方法:
类名.静态属性或方法
(2)extends 继承:
类名 extends 要继承的类名
类之间继承后,可以在子类里直接使用父类的所有数据和方法
(注意:在static静态方法里要new 后才能使用)
(3)继承类后,改写类里的方法:
6.9.2 class→interface
(1)直接用:
类调用接口:不需要调用就可以直接使用,如:
用法1:在代码里直接写上接口名;
用法2:(访问权限) 接口名 命名;
①在静态方法里调用的时候要添加static
②要重写接口的抽象方法
类调用接口的特性:
用法1:(修饰符) 常量类型 命名=接口名.常量名;
用法2:接口名.常量名;
(2)implements 实现:
格式:类名 implements 要实现的接口名
(其中,类可以实现多个接口,中间用”,”号隔开)
(3)调用后,改写接口里的方法
6.9.3 interface→class
(1)直接用:
接口调用类:访问权限 类名 命名=new 类名();
接口调用类的特性:
用法1:(访问权限) 常量类型 命名=new 类名().常量
用法2:类名 命名=new 类名();
常量类型 常量名=命名.常量();
(注意:变量和常量的调用方式相同,接口只能直接调用类的常量和变量)
6.9.4 interface←→interface
(1)直接用:
常量类型 常量名=接口名.常量名;
(注意:接口不能直接调用接口的抽象方法)
(2)extends 继承:
接口名 extents 要继承的接口名,要继承的接口名,...... //可继承多个接口
常量类型 常量名=常量名;//继承后直接使用父接口的数据,但不能调用抽象方法
6.10 Java垃圾回收
6.10.1 垃圾回收的对象
回收的对象:在堆上分配对象(空间)
6.10.2 强制垃圾清理
手动去调 System.gc( ) | Runtime.getRuntime( ).gc( )
通知垃圾回收器帮我们清理内存, 但是它不一定会完全的回收所有对象
6.10.3 垃圾自动回收流程
(1)"可达状态":创建一个对象时
(2)"可恢复状态":一旦没有被引用, 这个对象立即进行该状态
(3)”不可达状态”:当 JVM 内存紧张, 要回收一些内存, 某些 "可恢复状态"对象被调用finalize(),如果在 finalize() 没有任何恢复操作, 该对象进入 --> 不可达状态 [等待]--> 清理
(4)在被调用的finalize() 中可以对该对象恢复引用, 将置为 --> "可达状态",这样就不会被回收了
例子:
public class FinalizeTest{
int id;
private FinalizeTest ft1;
FinalizeTest( int _id ){
id = _id;
}
public static void main( String[] args ){
//没有要求 gc 去做清理的工作
for( int i=0; i<5; i++ ){
ft1 = new FinalizeTest( i );
//System.gc(); //强制 gc 回收无用的内存
Runtime.getRuntime().gc(); //强制 gc 回收无用的内存
}
ft1 = null; //把这个对象置为 --> 可恢复的状态
System.out.println( "程序已结束!" );
}
//是 Object 类的方法
public void finalize(){
//可以在这一个方法, 恢复对象的引用
ft1 = this; //这一个 this 就是将被回收的对象
System.out.println( "finalize: "+ id );
}
}
6.10.4 垃圾回收流程图