Object类
java是面向对象的,在java中所有的引用类型都默认继承自Object类
即Object类是所有引用类型的父类
所有的引用类型的默认值是null
在引用类型中 == 比较的是地址是否是同一个
toString方法:返回字符串
//重写Object类的toString方法
@Override
public String toString(){
return "this is a EasyObject";
}
finalize方法:是一个对象最后执行的代码(对象要被销毁时要执行的代码)
public static void main(String[] args) {
Object obj = new EasyObject();
System.out.println(obj.toString());
System.out.println(obj);
obj=null;
System.gc();//垃圾回收
}
@Override
protected void finalize(){
//finalize方法是一个对象最后执行的代码(对象要被销毁时要执行的代码)
System.out.println("对象被销毁");
}
基本数据类型的封装类(包装类)
java面向对象,所有的内存都是对象
int a = 12; 这里的a不是对象
基本数据类型记录的是数值,不是面向对象的
java为了实现万物皆对象的理念,给每一个基本数据类型提供了对应的封装类型
类型 | 基本数据类型 | 对应封装类型 |
---|---|---|
整数类型 | byte | Byte |
short | Short | |
int | Integer | |
long | Long | |
浮点型 | float | Float |
double | Double | |
字符型 | char | Character |
布尔型 | boolean | Boolean |
基本数据类型的封装类型 可以和基本数据类型无障碍转换
装箱:基本数据类型转换成对应的封装类型的过程
Integer i = 12;
Double d = 12.2;
拆箱:将封装类型转换成基本数据类型的过程
i = new Integer(22);
int aa = new Integer(22);
System.out.println(i+33);
基本数据类型的封装类型的缓存机制
基本数据类型的包装类是为了方便在使用基本数据类型的同时可以使用其它类的方法和特性。缓存机制的设置可以提高性能和节省内存空间。
当程序中频繁使用某个基本数据类型的包装类时,每次创建新的包装类对象都会消耗一定的性能和内存资源。为了避免频繁创建对象,基本数据类型的包装类在设计时使用了缓存机制。
缓存机制的具体实现是在包装类的内部创建了一个缓存数组,这个数组存储了一定范围的常用数值的对象。当需要使用包装类时,首先检查缓存数组中是否已经存在相应数值的对象。如果存在,则直接返回缓存中的对象,避免了额外的对象创建和内存消耗。
基本数据类型的封装类 | 缓存范围 |
---|---|
整数型(包括Byte、Short、Integer、Long) | -128~127 |
浮点型(Float、Double) | 没有缓存 |
字符型(Character) | 0~127 |
布尔型(Boolean) | 就两个值 |
只有Integer的缓存范围上限可以调整:
在VM初始化时设置参数-XX:AutoBoxCacheMax=<size>
译文:缓存以支持JLS要求的-128到127(包括-128和127)之间值的自动装箱的对象标识语义。缓存在首次使用时初始化。缓存的大小可以通过-XX:AutoBoxCacheMax=<size>选项来控制。在VM初始化期间,java.lang.Integer。IntegerCache.high属性可以设置并保存在sun.misc的私有系统属性中。VM类。
包装类的对象在缓存范围内直接赋值,比较的是缓存对象的地址,另外new出来的对象则不是。
Integer intA = 127;
Integer intB = 127;
System.out.println(intA==intB);//true
Integer intC = -129;
Integer intD = -129;
System.out.println(intC==intD);//false
Character charA = 0;
Character charB = '\u0000';
System.out.println(charA==charB);//true
封装类型之间没有父子类的关系,不存在互相转换,只有向上转型
Long l = 12L;
Number num = new Integer(12);
//必须先拆箱成基本数据类型后强转成对应基本数据类型再重新装箱
Byte b = (byte)(long)l;
System.out.println(b);
基本数据类型和封装类型比较:是将封装类型拆箱后再比较
Integer intN = 1200;
int iN = 1200;
System.out.println(intN==iN);
Short sN = 1200;
Double dN = 1200.0;
System.out.println(sN==iN);
System.out.println(dN==iN);
不同封装类型之间不能直接比较,不是同一个类,没有可比性,报错
new Byte("12");
new Short("12");
new Integer("12");
new Long("12");
new Float("12.0");
new Double("12.0");
new Character('a');//Character没有传入字符串的构造方法,只能传入字符
Integer.valueOf("12");//将字符串转换成Integer封装类型
Integer.parseInt("12");//将字符串转换成int基本数据类型
float f1 = Float.valueOf("12");
float f2 = Float.parseFloat("12");
System.out.println(f1);
System.out.println(f2);
包
声明包用 package 关键字,必须在第一行,前面不能有代码
导入包用 import 关键字,导入整个包或者包下的指定类
package com.easy717;//包声明,必须是全名,必须在第一行,前面不能有代码
import java.util.*;//导入包.*表示这个包下所有的类全部导入
所有的类默认引入java.lang包
类名重名的情况下可以使用类的全名(包名.类名)指定具体使用哪一个类
new com.easy.EasyA();
new com.easyb.EasyA();//重名类直接使用类全名。不需要导入包
static 静态的,类的
class StaticObject{
//static 静态的 类的
int age;//成员属性(对象的属性)
//静态方法中不能直接调用非静态的属性和方法
static void method(){
new StaticObject().age = 12;
}
static int maxAge;//静态属性(类的属性,所有成员共享),只能在类中声明
}
用static修饰表示是属于类的,使用类名直接调用static修饰的属性和方法,不需要创建实例对象
静态属性对所有对象是共享的,只能在类中声明
本类的对象也可以调用静态的属性和方法,但调用的方式还是静态方式(以类的名义调用)
静态方法中不能直接调用非静态的属性和方法
StaticObject staA = new StaticObject();
StaticObject staB = new StaticObject();
//static修饰的属性是属于类的 可以使用类名直接调用static修饰的属性和方法
StaticObject.maxAge=260;
//静态属性对所有的对象是共享的
//本类的对象也可以调用静态的属性和方法,调用的方式还是静态的方式
staA.maxAge=270;
System.out.println(staA.toString());//270
System.out.println(staB.toString());//270
抽象类
使用 abstract 关键字修饰的类是抽象类,抽象类是用来被继承的
1.抽象类没有直接实例(不能new)
2.抽象类中可以定义抽象方法
3.抽象方法没有方法体,被子类重写
4.实体类继承抽象类,必须实现抽象类中的抽象方法(重写)
5.抽象类继承抽象类,也可以不实现抽象方法
6.抽象类也可以继承实体类,如Object类
public abstract class EasyAbstract {
EasyAbstract(){}//抽象类中可以定义抽象方法
public void methodA(){//普通方法
}
//抽象方法 使用abstract修饰的方法
//抽象方法没有方法体,被子类重写
public abstract void methodB(int a,double b);
}
class Son extends EasyAbstract{
//实体类继承抽象类 必须实现抽象类中的抽象方法
public void methodB(int a,double b){
}
}
final 最终的
final的用途:
1.final可以修饰类,表示类不能被继承
2.final可以修饰方法,表示方法不能被重写
3.final可以修饰量(属性),表示不能被重新赋值(使用=)
有两种方式可以对final属性进行初始化:
一种是直接赋值,另一种是在构造方法中进行初始化
public class EasyFinal {
//final int height = 200;//一种是直接初始化
final int height;
//另一种是在构造方法中进行初始化
public EasyFinal(){
height = 200;
}
public static void main(String[] args) {
final int a = 12;
final int[] arr={1,2,3};
arr[2] = 44;//里面的内容可以改变
}
public void test(final int a){//表明参数不可重新被赋值,同修饰变量
}
}
这里总结前面几种与abstract存在矛盾的关键字
关键字 | 含义 | 约束 |
---|---|---|
abstract | 抽象的 | 没有实例,没有实现,需要被子类重写 |
private | 私有的 | 不能被类外访问 |
static | 静态的 | 属于类的,不能被重写 |
final | 最终的 | 不能被继承 / 重写 / 赋值 |
接口
接口声明使用 interface 关键字
要点:1.接口中只能定义方法,没有方法体
2.接口中的方法是抽象方法 ---没有具体实现的方法,默认使用public abstract修饰
3.接口中定义的属性都是常量,默认使用public static final来修饰
4.接口中没有构造方法,常量必须直接初始化
@FunctionalInterface
interface IVehicle extends Cloneable{
public static final String MAX_SPEED="20000";//常量
String MIN_SPEED="10";//常量必须初始化
//接口中定义的方法默认使用public abstract修饰
void transport();//用来被重写(实现)
//接口中可以定义default修饰的实体方法(jdk1.8后允许)
default void test(){//虽然使用default修饰,访问权限还是public
}
}
注:在JDK 1.8以后,接口中也可以定义 default 来修饰的实体方法,但访问权限还是 public
1.Java中使用 implement 关键字声明一个类实现一个接口,是对类的一种约束
2.一个类只能继承自一个类(单继承),但可以实现多个接口(多实现)用逗号隔开
3.接口可以使用 extends 来继承接口,不能继承类。抽象类可以实现也可以不实现接口
4.一个接口也可以继承多个接口
(当继承的多个接口中有方法的方法名和参数列表都相同但返回值不同时会有冲突就不行)
5.如果接口中只有一个未实现的方法,这个接口称为函数式接口
(可以使用 @FunctionalInterface 注解来验证)
深浅拷贝
克隆:是对象才能克隆,被克隆的对象必须实现 Cloneable 接口,不然会报异常。
Cloneable是一个空接口,起到标识作用
Object类中有clone方法,但是用protected修饰,访问不到
如果在类外使用clone方法需要重写
面试题:什么是深拷贝和浅拷贝?
浅拷贝:只拷贝对象自身,不拷贝它关联的属性(关联的属性直接指向相同的地址)
深拷贝:克隆自身的同时也要克隆关联的属性
public class Easy {
Teacher tea = new Teacher("崔强");
Student stu = new Student();
stu.teacher = tea;
System.out.println(stu);
Object obj = stu.clone();
System.out.println(obj);
stu.teacher.name = "王悦";
System.out.println(obj);
System.out.println(stu);
}
}
class Student implements Cloneable{
Teacher teacher;
//如果在类外使用clone方法需要重写
public Object clone() throws CloneNotSupportedException {
//深拷贝:克隆自身的同时也要克隆关联的属性
Object obj =super.clone();//将当前对象克隆一份
Object objTeacher = this.teacher.clone();//将当前对象的teacher属性克隆一遍
//将克隆出来的teacher对象赋值给克隆出来的Student对象中
((Student)obj).teacher = (Teacher)objTeacher;
return obj;
}
}
class Teacher implements Cloneable{
String name;
public Teacher(String name){
this.name = name;
}
//浅拷贝
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
浅拷贝没有拷贝关联属性,它和原对象的关联属性指向相同的地址,一个修改另一个也被修改。
深拷贝将被克隆对象的关联属性也单独拷贝了一份,二者相互独立,互不影响。