前言
❤实时更新❤
主要总结的部分是java后台的开发岗位
主要是通过刷各个公司以及市场上中的选择题笔记
以下都是博主经常错或者可能需要掌握的一些技术点
(主要是选择题某个难以理解的知识点或者是整个知识点都拷贝下来)
正文
以下选择题没有顺序重要性
1. 接口
接口中方法的默认修饰符时public abstract,抽象方法可是没有方法体的,没有大括号{}
JDK8中,接口中的方法可以被default和static修饰,但是!!!被修饰的方法必须有方法体
接口是可以多继承的,但抽象方法不能有方法体
2. 值传递和引用传递
主要通过代码进行考察
- 值传递主要是 字符串,String类是final类型的,不能继承和修改这个类
- 引用传递主要是通过对象、数组
3. 非静态变量
非静态变量不能够被静态方法引用
非静态成员只能被类的实例化对象引用,因此这里在静态方法中访问x会造成编译出错
public class Test
{
public int x;
public static void main(String []args)
{
System. out. println("Value is" + x);
}
}
4. JVM参数
- Xmx10240m:代表最大堆
- -Xms10240m:代表最小堆
- -Xmn5120m:代表新生代
- -XXSurvivorRatio=3:代表Eden:Survivor = 3 根据Generation-Collection算法(目前大部分JVM采用的算法),一般根据对象的生存周期将堆内存分为若干不同的区域,一般情况将新生代分为Eden ,两块Survivor; 计算Survivor大小, Eden:Survivor = 3,总大小为5120,3x+x+x=5120 x=1024
新生代大部分要回收,采用Copying算法,快!
老年代 大部分不需要回收,采用Mark-Compact算法
5. 字节流和字符流
区分它的判定可以通过
stream结尾都是字节流,reader和writer结尾都是字符流
字节流:
InputStream
|-- FileInputStream (基本文件流)
|-- BufferedInputStream
|-- DataInputStream
|-- ObjectInputStream
字符流
Reader
|-- InputStreamReader (byte->char 桥梁)
|-- BufferedReader (常用)
Writer
|-- OutputStreamWriter (char->byte 桥梁)
|-- BufferedWriter
|-- PrintWriter (常用)
6. final
- final修饰变量,则等同于常量
final修饰变量,变量的引用(也就是指向的地址)不可变,但是引用的内容可以变(地址中的内容可变)。 - final修饰方法中的参数,称为最终参数。
- final修饰类,则类不能被继承
- final修饰方法,则方法不能被重写。
5. final 不能修饰抽象类
6. final修饰的方法可以被重载 但不能被重写
注意重载和重写的区别
重写(Overriding)是父类与子类之间多态性的一种表现
,类的不同实现可以重写父类方法,实现同方法名,同参数,同返回类型,不同的实现。
重载(Overloading)最典型的就是一个类的不同构造函数,方法名相同,参数个数不同,返回类型也可以不同,重载是一个类中多态性的一种表现。
补充一下finalize
7. 多态
某个代码编译测试题
class Base
{
public void method()
{
System.out.println("Base");
}
}
class Son extends Base
{
public void method()
{
System.out.println("Son");
}
public void methodB()
{
System.out.println("SonB");
}
}
public class Test01
{
public static void main(String[] args)
{
Base base = new Son();
base.method();
base.methodB();
}
}
测试结果为编译不通过
new 了一个派生类,赋值给基类,所以下面的操作编译器认为base对象就是Base类型的
Base类中不存在methodB()方法,所以编译不通过
要想调用的话需要先通过SON son=(SON)base;强制转换,然后用son.methodB()调用就可以了。
8. jvm
一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法, 并且在下一次垃圾回收动作发生时,才会真正的回收对象占用的内存(《java 编程思想》)
在C++中,对象的内存在哪个时刻被回收,是可以确定的,在C++中,析构函数和资源的释放息息相关,能不能正确处理析构函数,关乎能否正确回收对象内存资源。
在java中,对象的内存在哪个时刻回收,取决于垃圾回收器何时运行,在java中,所有的对象,包括对象中包含的其他对象,它们所占的内存的回收都依靠垃圾回收器,因此不需要一个函数如C++析构函数那样来做必要的垃圾回收工作。当然存在本地方法时需要finalize()方法来清理本地对象。在《java编程思想》中提及,finalize()方法的一个作用是用来回收“本地方法”中的本地对象
java的堆内存分为两块:permantspace(持久带) 和 heap space
- heapspace分为年轻带和年老带
- 持久带中主要存放用于存放静态类型数据,如 Java Class, Method 等, 与垃圾收集器要收集的Java对象关系不大。
关于jvm的堆内存
- 年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存。设置的内存参数Xmx过小或程序的内存泄露及使用不当问题。这种情况下除了检查程序、打印堆内存等方法排查,还可以借助一些内存分析工具,比如MAT就很不错。
- 持久代溢出原因 -动态加载了大量Java类而导致溢出。解决办法唯有将参数 -XX:MaxPermSize 调大(一般256m能满足绝大多数应用程序需求)。将部分Java类放到容器共享区(例如Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量的共享类库
关于异常机制如果try语句里有return,返回的是try语句块中变量值。
详细执行过程如下:
- 如果有返回值,就把返回值保存到局部变量中;
- 执行jsr指令跳到finally语句里执行;
- 执行完finally语句后,返回之前保存在局部变量表里的值。
如果try,finally语句里均有return,忽略try的return,而使用finally的return.
9. servlet
- ServletContext对象:servlet容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。整个web应用只有唯一的一个ServletContext对象
- servletConfig对象:用于封装servlet的配置信息。从一个servlet被实例化后,对任何客户端在任何时候访问有效,但仅对servlet自身有效,一个servlet的ServletConfig对象不能被另一个servlet访问。
通过ServletConfig接口的getInitParameter()方法可以获得Servlet的初始化参数
10. 单例模式
具体更多的详情可看我这篇文章
JAVA设计模式之单例模式详细分析(全)
详情可看这道例题
class Chinese{
private static Chinese objref =new Chinese();
private Chinese(){}
public static Chinese getInstance() { return objref; }
}
public class TestChinese {
public static void main(String [] args) {
Chinese obj1 = Chinese.getInstance();
Chinese obj2 = Chinese.getInstance();
System.out.println(obj1 == obj2);
}
}
单例模式,obj1和obj2其实是一个对象,应该返回true!
11. 泛型
这道题感觉就是一个新世界,补充了很多知识点
题目如下
class A {}
class B extends A {}
class C extends A {}
class D extends B {}
下面的判断对与错
public static void main(String[] args) {
List<A> a;
List list;
list = a; //对,因为List就是List<?>,代表最大的范围,A只是其中的一个点
List<B> b;
a = b; //错,点与点之间不可相互赋值
List<?> qm;
List<Object> o;
qm = o; //对,List<?>代表最大的范围,List<Object>只是一个点
List<D> d;
List<? extends B> downB;
downB = d; //对,List<? extends B>代表小于等于B的范围,List<D>是一个点
List<?extends A> downA;
a = downA; //错,范围不能赋值给点
a = o; //错,List<Object>只是一个点,不可点赋值给点
downA = downB; //对,小于等于A的范围包含小于等于B的范围,B是A的子类
}
- 尖括号里的是一个类,那么尖括号里的就是一个点,比如List,List,List
- 尖括号里面带有问号,那么代表一个范围,<? extends A> 代表小于等于A的范围,<? super A>代表大于等于A的范围,<?>代表全部范围
- List<?>和List 是相等的,都代表最大范围
点不可相互赋值,除非是相同的点,小范围可以赋值给大范围,大范围不可赋值给小范围
12. ==
相对于字符串来说
String str1="hello";
//str1指的是方法区中的字符串常量池中的“hello”,编译时期就知道的
String str2="he"+ new String("llo");
//str2必须在运行时才知道str2是什么,所以它是指向的是堆里定义的字符串“hello”,所以这两个引用是不一样的
String str3="hello";
String str4=new String("Hello");
String str5="hel"+"lo";
System.out.println(str1==str2); //false
System.out.println(str1==str3); //true
System.out.println(str1==str4); //false
System.out.println(str1==str5); //true
String str1=“hello”; 这样创建字符串是存在于常量池中
String str2=new String(“hello”); str2存在于堆中
相对于包装类来说
Integer i01=59;
int i02=59;
Integer i03=Integer.valueOf(59);
Integer i04=new Integer(59);
==比较的是地址
但是当为基本类型时,比较的是值
如果俩边有包装类型,则先将包装类型转换为基本类型在比较值是否相等。
当俩边都为包装类型时,即为对象,比较的是地址
13. Math函数
Math.floor() 表示向下取整,返回double类型 (floor—地板)
Math.ceil() 表示向上取整,返回double类型 (ceil—天花板)
Math.round() 四舍五入,返回int类型
14. import
Java提供了包机制。包是类的容器,用于分隔类名空间。如果没有指定包名,所有的示例都属于一个默认的无名包。Java中的包一般均包含相关的类,java是跨平台的,所以java中的包和操作系统没有任何关系,java的包是用来组织文件的一种虚拟文件系统。
import语句并没有将对应的java源文件拷贝到此处仅仅是引入,告诉编译器有使用外部文件,编译的时候要去读取这个外部文件。
定义在同一个包(package)内的类可以不经过import而直接相互使用。
15. 数组
数组的写法可以这样写都对(注意第二种)
float f[][] = new float[6][6];
float []f[] = new float[6][6];
float [][]f = new float[6][6];
float [][]f = new float[6][];
代码问答题
class X{
Y y=new Y();
public X(){
System.out.print("X");
}
}
class Y{
public Y(){
System.out.print("Y");
}
}
public class Z extends X{
Y y=new Y();
public Z(){
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
(1)初始化父类的普通成员变量和代码块,执行 Y y=new Y(); 输出Y
(2)再执行父类的构造方法;输出X
(3) 初始化子类的普通成员变量和代码块,执行 Y y=new Y(); 输出Y
(4)再执行子类的构造方法;输出Z所以输出YXYZ
interface Com{
int M=200;
int f();
}
class ImpCom implements Com{
【代码】
}
将下列(A、B、C、D)哪个代码替换下列程序中的【代码】不会导致编译错误?
public int f(){return 100+M;}
int f(){return 100;}
public double f(){return 2.6;}
public abstract int f();
答案是第1个
1、必须实现接口中所有的方法。
在实现类中实现接口时,方法的名字、返回值类型、参数的个数及类型必须与接口中的完全一致,并且必须实现接口中的所有方法。
2、接口实现类相当于子类,子类的访问权限是不能比父类小的。