java学习(五)
文章目录
面向对象(下)
java基本数据类型包装类
[外链图片转存失败(img-vjIiNfPZ-1568981904775)(C:\Users\msi\AppData\Roaming\Typora\typora-user-images\1549177155001.png)]
通过这些包装类就可以实现引用传递基本类型参数,这些包装类可以直接与基本数据类型相互赋值
还可以通过包装类提供的
class.parseXxx(String s)//静态方法
Xxx(String s)//构造器
实现将字符串转换为数值
String类提供String.valueOf(class n)
方法可以把基本类型变量转换为字符串
在8位补码表示范围(-128~127)以外的Integer
包装类的两个对象即使数值相等,内存也不相等;相对的,补码表示范围以内的同数值的对象内存地址是相等的。因此,使用静态的compare
方法来比较对应的基本类型的数值是否相等
有关Object类
-
toString()方法
java所有类继承自
Object
类,因此都具备toString()
(可重写)等方法。Object
类提供的toString()
方法主要用于输出类或者对象的自我描述信息,默认返回该类的“类名+@+hashCode” -
==与equals()方法
==
类似cpp中的用法,equals()
(可重写)则专用于判断对象的值而非地址
单例类
- 构造函数用
private
修饰 - 定义一个
public static
方法(因为此类仅允许创建一个对象,且构造函数被private
修饰,故只能用类的方法来创建对象) - 定义一个
private static
成员变量用于缓存曾经创建过的实例(对象唯一)
final修饰符
-
变量
被
final
修饰的变量一旦赋了初始值就无法再次更改,因此final
修饰的成员变量必须被显式指定初值实例变量:定义时、普通初始化块、构造函数(普通函数不能给
final
变量赋值)类变量:定义时、静态初始化块
宏变量:在定义的时候就赋初值,直接被编译器编译为直接值
-
方法
被修饰的方法不可重写,但是可以被重载
-
类
被修饰的类不可以被继承
valueOf()
方法
该方法缓存一定范围内的对象,若新传入的值在缓冲区中已存在则不再生成新对象
抽象类
-
以
abstract
修饰 -
有抽象方法的类只能被定义为抽象类,而抽象类里面可以没有抽象方法
抽象方法写法:
public abstract void test();
-
抽象类不可实例化,但是含有构造器,其构造函数不可用于创建实例,主要是用于被子类调用,抽象类主要用于被子类继承
-
abstract
和final
是相对的,不可同时出现;abstract
修饰的方法不能同时被private
修饰,否则将使子类的继承变得毫无意义 -
主要优点是可以不用知晓具体类型进行强制类型转换,而是直接用抽象类的引用变量使用子类的方法(类似cpp的纯虚函数),比普通类更适合拿来做原型
比如定义抽象类
shape
,用triangle
和circle
继承它,以此发挥多态的作用
java8接口
其内部所有的方法都是抽象方法,不能含有普通方法,但java8允许定义默认方法,可以提供方法实现。
格式如下
public/default interface iname extends f1,f2
{
//instance definations(成员变量只能是静态常量)
//abstract method definations
//interior class、interface、enum definations(java8 之后)
//default method or class method definations
}
有关接口的定义:
- 接口内部成员的访问控制修饰符为
public
或省略(自动添加public
修饰符); - 对于接口内部的常量,系统会自动添加
static
和final
修饰符,即总是会自动变成public static final
; - 对于内部的普通方法会自动添加
abstract
修饰符; - 普通方法不可以有方法体(不能实现,因为是抽象的)、而类方法(
static
修饰)、默认方法(default
修饰)必须有方法体(必须实现) - 接口内部不含构造器和初始化块的定义
- 接口中的内部类、内部接口、内部枚举默认都采用
public static
修饰 - 和类一样,一个
.java
文件只能有一个public
接口,且需要和.java
文件名相同 - 如果匿名内部类所创建的接口实例只需要实现一个抽象方法,则该接口称为函数式接口
接口的继承:
- 接口支持多继承,如:
interface iC extend iA, iB
接口使用:
-
接口不可实例化,但可以被实现类实现,一个类可以实现多个接口。(模拟多继承)
如:
class A extends parent_class_B implements interface_C, interface_B, ...
这个实现必须实现接口内的所有抽象方法,方法的修饰符只能是
public
(因为接口的抽象方法是public
) -
接口类型的引用变量可以直接赋值给
Object
类的引用变量(为了实现多继承下的多态),如interface A = new Class();
java内部类
特性如下:
-
更好的封装:同一个package的其他类无法访问该内部类。
-
访问
private
成员:内部类被视为外部类的成员,同一类的成员间可以相互访问,但外部类不能访问内部类的成员变量(外部不能查看内部细节) -
匿名内部类:仅使用一次的内部类,通过
new 父类构造器(parameter)或者 接口()
来定义,并且必须实现其内部的所有抽象方法。(省略了extends
和implements
关键字),他只有隐式默认构造器,无法传递参数。 -
额外的修饰符:
private
、protected
、static
-
编译会单独生成
OuterClass$InnerClass.class
文件
-
非静态内部类访问外部类:类的非静态内部类可以直接访问外部类的
private
的成员,其实例必须寄生在外部类的实例中,内部类实例保存有它所寄生的外部类对象的引用内部类访问同名的外部类实例变量的成员变量:
外部类类名.this.成员变量名
(编译器从内向外查找变量)非静态内部类中不能有任何静态声明
-
外部类访问非静态内部类:外部类需要显式创建非静态内部类对象来调用访问其实例成员。
-
在外部类以外的地方创建非静态内部类对象:
OuterInstance.new InnerConstructor()
,即:非静态内部类的构造器必须通过外部类对象来调用,因为寄生关系。因此,当新的类继承非静态内部类时,必须先创建一个外部类对象并将该对象传入新的类的构造器中。
以下是其他类继承内部类,实例化对象的固定模板格式:
class Out { public Out() { } class In { public In(String msg) { System.out.println(msg); } } } public class SubClass extends Out.In { public SubClass(Out out) { out.super("hello"); //必须用 “外部类.super()”这一形式,调用SubClass的父类Out.In来显式调用内部类的构造器 } public static void main(String[] args) { Out out = new Out(); SubClass test = new SubClass(out); } }
-
用
static
修饰的类为静态内部类,它属于外部类本身。(只能是内部类,外部类无法用static
修饰)创建时,直接
new 外部类名.内部类构造器
即可(类似包空间) -
接口也能定义内部类,但只能是静态内部类(默认
public static
),实用意义不大。 -
若要在外部类的静态方法如
public static void main(String args[])
中,实例化非静态内部类对象,则需要先实例化外部类对象,再实例化非静态内部类对象,因为普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。或者将非静态内部类改为静态内部类。
-
局部内部类:定义在方法中的内部类,不能用访问控制符和
static
修饰,仅在该方法内有效。同一个类可以有不止一个局部内部类。生成的.class
文件名为OuterClass$NInnerClass.class
其中,N代表局部内部类的数字序号。
lambda表达式
本质是对函数式接口实例化的简化。
-
使用:
主要作用是代替匿名内部类的语法,省略匿名内部类对象的创建代码,简化为只有一个
->
箭头函数。当箭头函数主体只有一句代码的时候,可以直接省略大括号。
当函数只有一个参数时,可以省略参数的小括号。
可以省略return。
-
限制:
lambda表达式的得到的目标对象类型(编译时指定的对象类型)必须是明确的函数式接口。
lambda表达式只能为函数式接口创建对象——即只能有一个方法。
例如:
@FunctionalInterface interface Converter{ Integer convert(String from); } Converter converter1 = from -> Integer.valueOf(from); Integer val = converter1.convert("99"); System.out.println(val);//输出int 99
即利用lambda表达式返回得到了一个函数式接口的对象converter1。
-
高级特性:方法引用与构造器引用,前后参数一一对应。
方法引用:
-
引用类方法:格式为
类名::类方法
根据上面的例子,有
Converter converter1 = Integer::valueOf;
-
引用特定对象的实例方法:格式为
特定对象::实例方法
例如
Converter converter2 = from -> "fkit.org".indexOf(from);
可以简写为Converter converter2 = "fkit.org"::indexOf;
-
引用某类对象的实例方法:格式为
类名::实例方法
例子如下
@FunctionalInterface interface MyTest { String test(String a, int b, int c); } MyTest mt = (a, b, c) -> a.substring(b, c); String str = mt.test("Java I Love you", 2, 9);
可以替换为
MyTest mt = String::substring;
以达到相同的目的。 -
引用构造器:格式为
类名::new
例子如下
@FunctionalInterface interface YourTest { JFrame win(String title); } YourTest yt = (String a) -> new JFrame(a); JFrame jf = yt.win("My window");
可以替换为
YourTest yt = JFrame::new;
函数式接口内的抽象方法的参数会传递给类的相应构造器。
-
-
lambda和匿名内部类的区别
- 匿名内部类可以为任意接口、抽象类、普通类创建实例,而lambda表达式只能为函数式接口创建实例
- 匿名内部类可以调用接口中的默认方法,而lambda表达式不允许
枚举类
用enum
关键字定义,enum
关键字和class
、interface
地位相同
-
和普通类的区别
- 默认继承
java.lang.Enum
类,因此无法显式继承其他父类 enum
定义,非抽象的枚举类默认用final
修饰,无法派生子类- 枚举类的构造器只能是
private
修饰,可以省略不写 - 枚举类的实例必须在第一行显式列出,编译器自动添加
public static final
修饰 - 枚举类的实例只能是枚举值
- 枚举类可以定义抽象方法,编译器会自动为他加上
abstract
修饰符,但用户不能添加该修饰符(因为这样就无法写出方法的实现)并由他自己的不同枚举值实现这个方法,语法类似匿名内部类
- 默认继承
-
访问、遍历方式
使用foreach循环的时候,可以使用
EnumClass.values()
来迭代所有实例也可以直接用
EnumClass.variable
的形式,直接访问 -
枚举类通常应设置为不可变类,其成员变量不应该允许外部改变
public enum Gender { MALE("男"),FEMALE("女");//此处枚举值,即枚举类实例将会调用privte的构造器 private final String name; private Gender(String name) { this.name = name; } public String getName() { return this.name; } }
-
枚举类可以实现接口,且对于每个不同的枚举值,可以以不同形式实现接口内的方法
interface GenderDesc { void info(); } public enum Gender implements GenderDesc { MALE("男") {//大括号内实际上可以视为匿名内部类的类体 public void info() { System.out.println("For Male."); } }, FEMALE("女") { public void info() { System.out.println("For Female."); } }; ... }
可以看出语法类似匿名内部类
垃圾回收
-
只负责回收堆内存中的对象,不回收任何物理资源(数据库连接、网络IO等)
-
程序无法精准控制垃圾回收,只有当对象永久性失去引用才会在合适的时间被回收
-
垃圾回收之前总会调用对象的
finalize
方法,可以通过finalize
方法使得对象重新获得引用 -
程序可以通过
System.gc()
以及Runtime对象的方法Runtime.getRuntime().gc()
通知系统强制回收垃圾。 -
有关
finalize
方法:原则上永远不要主动调用某个对象的
finalize
方法,该方法是专门留给垃圾回收调用的;finalize
不一定会被执行;JVM执行
finalize
出现异常的时候,垃圾回收机制不会报告异常,程序会继续执行Runtime.getRuntime.runFinalization()
或System.runFinalization()
可以强制垃圾回收机制调用可恢复对象的finalize
方法 -
对象的软、弱和虚引用:
强引用:常见的引用方式,不可被垃圾回收
软引用:空间不足时,可能被回收,对内存敏感的程序很常见
弱引用:类似软引用,但优先级更低,只要启动了垃圾回收,就会被回收
虚引用:类似于无引用,常用于跟踪垃圾回收状态
当对象被回收后,其引用存在于引用队列中
JAR包
好处:
- 安全,设置数字签名
- JAR包除了内部会多一个名为
META-INF/MANIFEST.MF
的清单文件以外,其他完全和ZIP完全相同
使用:
-
创建:
jar cf xxx.jar xxx
,将当前路径下的xxx
路径下的全部内容压缩生成一个xxx.jar
文件创建可运行的jar包:
jar cvfe test.jar test.Test test
,把当前路径下的test
路径下的所有文件压缩到test.jar
中,并由e
选项指定test.Test
类作为程序入口 -
解压:
jar xf xxx.jar
-
更新:
jar uf xxx.jar xxx.class
需要提示信息加上
v
选项就可以了 -
运行可执行jar包:
java -jar test.jar
或者javaw test.jar