java基础总结(java面试一)
八大数据类型:
- 字节型
- byte(Byte)/8
- 浮点型
- float(Float)/32
- double(Double)/64
- 布尔型
- boolean(Boolean)/~
- 字符型
- char(Character)/16
- 整型
- short(Short)/16
- int/(Integer)32
- long(Long)/64
- boolean 只有两个值:true,false。可以使用1bit来存储。但是具体大小没有明确规定。
- JVM会在编译时期把boolean类型转换为int,true=1,false=0,java支持boolean数组但是是通过byte数组来实现的。
包装类型:
-
- Integer x=2;自动装箱:Integer.valueOf(2)
- int y=x;自动拆箱:X.intValue()
缓存池:
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
- new Integer(123) 每次都会新建一个对象;
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
IntegerCache缓存池默认是-128-127
也可以在JVM中修改上限 XX:AutoBoxCacheMax=<size>来指定上限大小。
String:
- java8中String是由final修饰,char数组
- java9中用byte数组并使用coder标识使用了哪种编码
- value数组被声明成final,这意味着value初始化以后就不能被修改了,因此保证了不可变性。
String用final修饰的好处:
- 可以缓存哈希值
- 因为String的hash经常被使用,比如HashMap中的key,不可变特性保证了只计算一次和hash值不变。
- StringPool的需要
- 如果一个String对象被创建过了,那么就会从StringPool中取得引用,只有String是不可变的,才可能使用StringPool。
- 安全性
- String 经常作为参数,String的不可变性保证了参数的不可变性。
- 线程安全
- 不可变性,天生具备线程安全。
String,StringBuffer,StringBuilder区别
- 可变性
- String不可变
- StringBuffer和StringBuilder可变
- 线程安全
- String线程安全
- StringBuffer线程安全(内部使用了Synchronized同步)
- StringBuilder线程不安全
StringPool
- 字符串常量池(StringPool), 保存着所有字符串字面量,这些字面量在编译时期就确定,不仅如此还能使用String的intern()方法在运行过程中向StringPool中插入新的字符串。
- 当一个字符串调用intern()方法时,如果StringPool中已经存在一个字符串通过equals比较相等。那么就会返回StringPool中的引用,否则创建一个新的,返回新的引用。
- 简而言之String中的intern方法就是StringPool中如果有这个字符串就返回这个字符串的引用,如果没有,就新建一个然后返回新的引用。
- java7之前中StringPool被放到常量池中也就是永久代,java7之后放到堆中,因为永久代空间有限,大量字符串被创建容易出现OOM。
new String(“ABC”)
- 使用这种方式一共会创建两个对象(前提是Stringpool中没有ABC的对象)
- “ABC”输入字符串字面量,因此会在StringPool中创建一个字符串对象,指向这个ABC字符串字面量
- 而使用new会在堆中创建一个对象。
运算
- 参数传递
- java的参数是以值传递的形式传入到方法中,而不是引用传递。
- float和double
- java不能隐式的执行向下转型,因为这会使得精度降低
- 字面量属于double类型,不能直接将1.1直接赋值给float,因为这是向下转型。
- 1.1f才是float类型
- 隐式类型转换
- 字面量1是int类型,不能直接使用short转换
- short s1 = 1; // s1 = s1 + 1;
- 但是可以使用++,--,或者强转。
switch
- 从java7开始,可以使用switch的条件判断语句使用String对象
- String s = "a"; switch (s) { case "a": System.out.println("aaa"); break; case "b": System.out.println("bbb"); break; }
- Switch不支持long,因为switch的设计初衷就是针对少数几个值的类型进行等值判断。如果过于复杂还是用if比较合适。
关键字
- final
- 数据
- 声明数据时为常量,可以是编译时常量也可以是运行时初始化后就不能被修改的常量。
- 对于基本类型,final使数值不变
- 对于引用类型,final使引用不变,也就是不能引用其他对象,但是被引用的对象可以修改。
- 方法
- 方法不能被子类重写。
- private的方法隐式的被指定为final,如果在子类中定义一个方法和基类中的一个private签名相同,此时子类的方法不是重写,而是一个新的方法。
- 类
- 不能被继承。
- 数据
- static
- 静态变量
- 静态变量:又叫类变量,也就是说这个变量属于类,类中的所有实例都可以通过类名访问该静态变量,在内存中值存一份。
- 实例变量:每创建一个实例,就会产生一个实例变量,它与该实例同生死。
- public class A { private int x; // 实例变量 private static int y; // 静态变量 public static void main(String[] args) { // int x = A.x; // Non-static field 'x' cannot be referenced from a static context A a = new A(); int x = a.x; int y = A.y; } }
- 静态方法
- 静态方法在类加载的时候就已经存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说不能是抽象方法。
- public abstract class A { public static void func1(){ } // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static' }
- 只能访问所属类的静态字段和静态方法,方法中不能有this,和super关键字,因为这两个关键字与具体对象关联。
- 静态语句块
- 静态语句块在类初始化的加载一次。
- 静态内部类
- 非静态内部类创建依赖于外部类实例,也就是说只有外部类实例创建后才能创建非静态内部类,静态内部类不需要。
- 静态内部类不能外部类的非静态方法和非静态变量。
- 静态导包
- 初始化顺序
- 静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
- 存在继承的情况下,初始化顺序为:
- 静态变量
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
Object方法
- equals
- 等价关系
- 自反性
- 对称性
- 传递性
- 一致性
- 与null的比较
- 等价与相等
- 对于基本类型,==判断两个值是否相等,基本类型没有equals方法
- 对于引用类型,==判断是否引用同一个对象,equals引用的对象是否等价。
- 实现
- 检查是否为同一个对象的引用如果是直接true
- 检查是否为同一类型,如果不是直接false
- 将object对象进行转型
- 判断每个关键域是否相等
- public class EqualExample { private int x; private int y; private int z; public EqualExample(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqualExample that = (EqualExample) o; if (x != that.x) return false; if (y != that.y) return false; return z == that.z; } }
- hashCode()
- hashCode()返回hash值,而equals是用来判断两个对象是否是等价,等价的对象散列值一定相同,但是散列值相同的两个对象不一定等价,这是因为计算哈希值具有 随机性,两个值不同的对象可能计算出相同的哈希值。
- 在覆盖equals方法时,应当总是覆盖hashCode()方法,保证等价的两个对象的hash值也相等。
- hashSet和HashMap等集合类使用hashCode来计算对象应该存储的位置,因此要将对象添加到集合类需要将对应的对象添加hashCode方法。
- 理想的哈希函数应道具有均匀性,不相等的对象应当均匀地分布在所有可能的hash值上,这就要求hash函数要将所有域的值考虑进来,可以把每个域都当成R进制的某一位,然后组成一个R进制的整数。
- R一般是31,奇素数。偶数会溢出,信息丢失。
- toString
- 默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示
- clone
- cloneable
- clone是Object的projected函数,他不是public,一个类不显示的去重写clone,其他类就不能直接去调用该类实例的clone方法。
- 重写clone方法,必须实现cloneable接口,这只是一个接口规范,如果一个类没有实现CloneAble接口,又重写了clone方***抛出异常,CloneNotSupportedException。
- 浅拷贝
- 拷贝对象和原始对象的引用类型是同一对象。
- 深拷贝
- 拷贝对象和原始对象引用类型不是同一对象
- clone的替代方案
- 使用clone方法来拷贝一个对象不好,会抛出异常还有类型转换。
- 可以使用拷贝 构造函数和拷贝工厂来拷贝一个对象
- 等价关系
继承
- 访问权限
- private,projected,public,default
- 抽象类和接口
- 抽象类:使用abstract修饰,一个类中包含抽象方法那么这个类必须声明为抽象类。
- 抽象类和普通类的最大区别是抽象类不能被实例化,只能被继承。(extends)
- 接口(interface使用implements实现)
- java8之前可以看成完全抽象的抽象类,java8以后可以拥有默认的方法,接口成员默认public,字段默认static,final。
- 比较
- 从设计层面抽象类是一种IS-A方式,需要满足里式替换原则,子类必须能够替换所有的父类对象
- 接口是LIKE-A,只提供方法实现契约,不要求IS-A关系。
- 一个类可以实现多个接口,只能继承一个抽象类
- 接口中成员方法只能是public,字段是static final,而抽象类可以是其他的。
- 使用选择:
- 接口
- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现Compareable接口中的compareTO方法。
- 需要多重继承。
- 抽象类
- 需要在几个相关类***享代码
- 需要控制访问权限
- 需要继承非静态和非常量的字段
- 大多数情况下接口优于抽象类,在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。
super
访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始 化的工作。应该注意到,子类一定会调用父类的构造函 数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它构造函数,那么就可以使用 super() 函数。访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
重写与重载
- 重写Override
- 存在于继承体系,指子类实现了一个与父类在方法声明上完全相同的方法。
- 为了满足里式替换原则,重写有三个限制:
- 子类方法访问权限必须小于等于父类
- 返回类型必须相等或者是子类型
- 抛出异常,相等或者子类。
- 使用 @Override 注解,可以让编译器帮忙检查是否满足上面的三个限制条件。
- 调用顺序优先级
- 先找自己的
- 找不到,然后找父类的
- 在找不到,参数转型成父类的找自己
- 再找不到,参数转成父类,找父类。
- this.func(this)
super.func(this)
this.func(super)
super.func(super)
- 重载Overload
- 存在于同一个类中,方法名称相同,但是参数类型,顺序,个数至少有一个不同
- 返回值不同,其他都相同的不算重载,有二义性。
反射
- 每个类都有一个Class对象,包含着与类有关的信息,当编译一个新类时,会产生一个同名的.class文件,该文件内容保存着Class对象。
- 类加载,相当于Class加载,类在第一次使用时才动态的加载到JVM中,也可以使用Class.forName("com.mysql.jdbc.Driver")控制类加载,会返回一个Class对象。
- 反射可以提供运行时类信息
- Class和java.lang.reflect一起对反射提供支持,java.lang.reflect类库,主要包括以下三个类:
- Field:可以使用get()和set()方法读取和修改Field对象关联的字段。
- Method:可以使用invoke调用与Method对象关联的方法。
- Constructor:可以使用Constructor的newInstance()创建新的对象。
反射优点:
- 可扩展性:应用程序可以利用全限定名创建可扩展对象实例
- 类浏览器和可视化开发环境
- 调试器和测试工具
反射缺点:
- 性能开销
- 安全限制:必须在没有安全限制的环境使用反射
- 内部暴露:私有属性可以被暴露
异常
- Throwable分为Error和Exception,其中error表示JVM无法处理的错误
- Exception分为受检时异常和未受检时异常
- 受检时异常:需要用try-catch 捕获处理
- 非受检时异常:程序运行时的错误,比如RuntimeException和IOException
- 关键字:
- throws,try,catch,finallt,throw.
泛型
- 泛型是什么,使用泛型的好处?
- 类的参数化,好处是提供了编译时的安全,避免出现类型转换异常。提高复用。
- 泛型是如何工作的,什么是类型擦除?
- 泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关信息,所以在运行时不存在任何类型的信息,例如在List<String>,在运行时仅用一个List来表示,这样做的目的是确保和java5之前的版本开发二进制类库进行兼容,你无法在运行时访问到类型参数,因为编译器已经把泛型转换成了原始类型。
- 什么是泛型中的限定通配符和非限定通配符?
- 限定通配符分为两种:
- <?extends T> 它通过确保类型必须是T的子类,确保类型的上界,可以get元素
- <? super T>它通过确保类型必须是T的父类,来确保下界。可以add元素
- <?>表示非限定通配符,因为<?>可以被任意类型替代。
- 限定通配符分为两种:
- List<? extends T> 和List<? super T>区别?
- 第一个可以接受任何父类是T的List
- 第二个可以接受任何子类是T的List
- 如何编写一个泛型方法,让他能够接受泛型参数并返回泛型类型?
- 需要用泛型类型替代原始类型
- KV, TE,类型占位符。
- 需要用泛型类型替代原始类型
- java中如何使用泛型编写带有参数的类?
- 关键仍然是使用泛型类型来代替原始类型,而且要使用JDK中采用的标准占位符。
- 编写一段泛型程序来实现LRU缓存?
- LinkedHashMap可用来实现固定大小的LRU缓存
- 你可以把List<String>传递给一个接受List<Object>参数的方法吗?
- 不可以,Object可以存任何对象,String只能存String。
- Array中可以用泛型吗?
- 不可以,Array不支持泛型,所以建议使用List来替代Array.
- 如何阻止java中类型未检查的警告?
- List<String> rawList = new ArrayList()
https://cloud.tencent.com/developer/article/1178629
注解
- Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
java和C++的区别
- java面向对象,c++为了兼容c,面向对象也面向过程
- java通过JVM实现跨平台,C++依赖特定的环境
- java没有指针,有引用,C++有指针
- java支持垃圾自动回收,C++手动回收
- java不支持多继承,只能通过多个接口来达到相同的目的,而c++支持多重继承。
- java不支持操作符重载