JavaSE
一、Java发展史
- 诞生1995年---------->SUN公司发明(被Oracle甲骨文收购)-------->1996JDK(Java开发工具包)诞生
- Java三大板块:JavaSE(Java标准版)、JavaEE(Java企业版)------>JavaME(Java微型版) JavaDE是基础
二、Java语言特性
-
简单性(不再继承多继承)、面向对象、可移植性(后面会详细讲)、多线程、安全性、健壮性(GC机制)
Java底层是C++实现的,纯面向对象的过程。
三、Java语言的编译及执行
-
Java的运行阶段主要包括两个主要阶段:编译阶段(源文件 .java文件)和运行阶段(字节码文件 .class)
-
编译阶段过程: 程序员需新建一个 .java扩展名的文件,该文件称为Java源文件,源文件中编写的是Java源代码/源程序,源代码必须符合Java语法规则,程序员需使用JDK中自带的 javac.exe 命令进行Java程序编译( javac + java源文件的路径),产生 .class文件。(这里要注意,生成java.class文件后,java源文件删除并不会影响java程序的执行,我们最终执行的文件是 .class文件,我们可以将class文件拷贝到其他操作系统中 【跨平台】)
编译阶段的作用: 主要任务是检查Java源程序是否符合Java语法,符合则生成字节码文件,否则,无法生成。
-
运行阶段过程: 找到生成的A.class文件在DOS窗口输入 java A 命令,java.exe命令会启动Java虚拟机(JVM),JVM会启动类加载器(classLoader),ClassLoader会去硬盘去寻找A.class文件,找到该文件后将该文件装到JVM中,JVM将A.class字节码文件解释称二进制这样的数据,然后操作系统执行二进制和底层硬件平台进行交互。
-
Java中有一句话相信大家已经再熟悉不过了,“一次编译,到处运行”,也就是说java程序可以在windows运行,也可以在Linux中运行,怎么实现这个过程?SUN公司想了一个办法,让java程序运行在一台虚拟的计算机中,这个虚拟机就是我们上面所说的JVM,通过他将.class文件编译成二进制,然后通过操作系统和硬件打交道。
【补充:JDK下包含很多命令,包含javac、java等命令】
四、Java标识符的命名标准和C语言有什么区别?
-
不同之处:Java标识符必须由数组、字母、下划线、美元符号组成,而C语言没有美元符号。
Java命名规范:类名、接口名,首字母必须大写,后面每个单词首字母大写;变量名、方法名是首字母小写,后面每个字 母大写;常量名是全部大写。
-
相同之处:开头不能是数字,容易产生二义性。
五、Java有几种基本类型?占多少字节?数据类型的作用是什么?
-
Java基本数据类型包含byte(1)、short(2)、int(4)、long(8)、float(4)、double(8)、boolean(1)、char(2)
C的基本数据类型包含char(1)、short(2)、int(2或4)、long(4)、long long(8)、float(4)、double(8)、long double(8或16)、bool(1)
-
作用:每一个数据都是相关类型的,不同的数据类型的数据占空间大小不同,作用是指导JVM在运行程序的时候给该数据分配多大的空间
-
补充:小容量转大容量,叫自动类型转换;大容量转小容量,叫强制类型转换。
六、什么是短路现象?
-
现象一:表达式1 && 表达式2 && 表达式3;
只有当表达式1为真(非0)时才需要判断表达式2的值;只有表达式1和2都为真(非零)时;才需要判断表达式3的值。当表达式1为假(0),则发生短路,不执行表达式2和3,整个表达式值为0!
-
现象二:表达式1 || 表达式2 || 表达式3;
只有表达式1为假(0)时才需要判断表达式2的值;只有表达式1和2都为假(0)时,才需要判断表达式3的值。当表达式1为真(非0),发生短路,不执行表达式2和3,整个表达式值为1!
七、Java的输Scanner是SDK1.5新增的一个类,可使用该类创建一个对象。
Scanner next =new Scanner(System.in);//Scanner是SDK1.5新增的一个类,可使用该类创建一个对象
然后next对象调用下列方法(函数),读取用户在命令行输入的各种数据类型
next.Byte(),nextDouble(),nextFloat,nextInt(),nextLine(),nextLong(),nextShot()
八、说一下方法执行过程的内存分配情况
- 前提知识:JVM内存划分上有三块主要的内存空间:方法区内存、堆内存、栈内存
- 方法代码片段属于.class字节码文件的一部分,字节码在类加载的时候,被放到方法区内,所以JVM中的三块主要的内存空间中方法区内存最先有数据,存放了代码片段。代码片段虽然在方法区只有一份,但可被重复使用,每一次调用这个方法时,需给该方法分配独立的栈内存,压栈,待方法结束后,内存释放,弹栈。
九、什么是方法重载?什么是方法重写?
-
重载:overload,特点:只和参数列表有关
public void m1(int a,int b) public void m1(double a,double b)//重载 区分: public void m1(int a,int b) public void m1(int b,int a)//这不叫重载 区分: public void m1(int a,int b) public int m1(int a,int b) //不叫重载
-
重写:Override 特点:就是在子类中把父类本身有的方法重新写一遍
public class Father { public static void main(String[] args) { // TODO Auto-generated method stub Son s = new Son(); s.sayHello(); } public void sayHello() { System.out.println("Hello"); } } class Son extends Father{ @Override public void sayHello() { // TODO Auto-generated method stub System.out.println("hello by ");//重写他爹的sayHello() } }
-
共同之处:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性
十、说一下面向过程和面向对象的区别
-
面向过程:关注点在于实现的具体过程,因果关系
优点:对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本低
缺点:采用面向过程的方法开发很难解决非常复杂的业务逻辑,另外面向过程的方法导致软件元素之间的耦合度非常高,一个环节出
问题,整个系统会遭到影响,扩展性差,很难达到组件复用。
-
面向对象:关注点在于对象能完成哪些功能
优点:耦合度低,扩展性强,更容易解决现实世界的复杂性问题,组件复用强。
缺点:前期投入成本高,需要大量的系统分析与设计,进行独立体抽取。
十一、详解面向对象的三大特征
-
封装:是包装一个事务,使外界不知道该事物的具体内容。
-
继承:就是能够直接获得已有的性质和特征,不必重复定义他们,子类自动地共享父类中定义的数据和方法的机制 。作用:代码复用
注意:私有的不能继承、构造方法不能继承,假如一个类没有显示继承任何类,那么默认继承Object类。
-
多态:多态本意是”有许多形态“,希腊语。指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。也就是说,在类等级的不同层次中可以共享一个行为的名字,然而不同的层次中的每个类却各自按自己的需要来实现这个行为。当对象接收到发送给它的消息时,根据该对象所属于的类动态选用在该类中定义的实现算法。
作用:降低程序的耦合度,提高程序的扩展里,能够使多态尽量使用太多,向上转型(子类->父类) 向下转型(父类->子类)
十二、什么是对象,什么是类?
- 拿一个圆做例子,有半径、周长、颜色、位置,我们现在有3个不同的圆,每个圆就是一个不同的的对象。但是他们都有相同的数据(半径、周长…)和相同的操作(显示自己、缩小半径等),因此他们是同一类事物,用Circle类来定义,而3个不同的圆可以声明为circle1,circle2,circle3,是具体的对象。实例就是由某个特定的类所描述的一个具体的对象,类是抽象的,并不是真是存在的,谁也没有见过抽象的圆。换句话来说,类就是建立对象时使用的**”样板“,按照这个样本所建立的一个个具体的对象,就是类的实际例子,成为实例**。
十三、JVM三大分区分别存储什么?
-
new 运算符的作用是创建对象,在JVM堆内存中开辟新的内存空间,里面还包含对象的一些属性高,垃圾回收主要针对堆内存.
-
方法区 内存在类加载的时候,class字节码代码片段被加载到该内存空间中**,静态代码块**也在方法区
-
栈内存 存方法代码片段执行的时候,会给该方法分配内存空间,并压栈
-
常量池 主要存放常量,String字符串主要放在方法去的常量池中
举例子: int i=10; Student s=new Student();//Student 对象包含age、no、sex等变量
用户通过调用s引用的地址,来访问堆内存的实例变量和具体对象,可以说堆内存主要是为了存储对象,而栈中主要存储一些基本数据类型变量和引用。 注意:在java语言中,程序员不能直接操作堆内存,java没有指针,所以只能通过”引用“区去访问堆内存当中对象内部的实例变量
十四、为什么不能通过类名直接访问实例变量?
-
成员变量分为实例变量和静态变量,实例变量是对象级别的变量,又被称为对象变量,new运算符的作用就是创建对象,在JVM堆内存中开辟新的内存空间,实例变量因为是对象级,所以不能通过类名.的形式直接访问,必须通过创建对象来访问。静态变量其实可以理解为就是带static的变量,可以被类型.直接访问。
-
变量可分为局部变量和成员变量
-
在类中的位置不同
成员变量:类中方法外
局部变量:方法定义中或者方法声明上 -
在内存中的位置不同
成员变量:在堆中
局部变量:在栈中 -
生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失 -
初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值,然后才能使用
-
十五、说一下构造方法的作用以及结构?
- 构造方法是没有返回值类型的,且构造方法的方法名必须和类名一致.如果加上返回值类型,那就是个普通方法.
- 作用:通过构造方法的调用来创建对象,同时初始化实例变量的内存空间.
- 怎么调用构造方法? 通常我们进行new 类名的时候,系统会自动调用无参构造方法.当然你也可以自定义无参或有参
- 构造方法实际上结束之后都有返回值,只是我们不需要写.系统自动返回.
- 也就是说**,实例变量的内存空间是在构造方法执行过程中完成开辟的.**
十六、说一下this和super的区别
-
Java当中this和super的区别:
- 属性的区别:
(1)this访问本类中的属性,如果本类没有这个属性则访问父类中的属性。
(2)super访问父类中的属性。- 方法的区别:
(1)this访问本类中的方法,如果本类没有这个方法则访问父类中的方法。
(2)super访问父类中的方法。- 构造的区别:
(1)this调用本类构造构造,必须放在构造方法的首行。
(2)super调用父类构造,必须放在子类构造方法首行。
(3)其他区别:this表示当前对象。super不能表示当前对象 -
什么时候this不能省略?
用来区分局部变量和实例变量的时候,this.不能省略。
十七、说一下静态static 和静态代码块有什么用?
-
什么时候用static修饰?-----所有对象都会用到这个属性,建议使用static来修饰,节省内存开销。
-
静态代码块格式:static{ 语句 ;}
作用:静态代码块在类加载时执行,并只执行一次,在一个类中可以编写多个,并遵循从上而下的顺序执行。
用在哪?比如项目中要求在类加载时执行代码完成日志记录,那么这段记录日志的代码就可以编写到静态代码当中,完成日志记录。
静态代码是java为程序员准备的一个特殊的时刻,这个特殊的时刻被称为类加载时刻,若希望在此刻执行一段特殊的程序,这段程序可以直接放到静态代码块中。
-
注意,静态代码中无法直接访问实例变量和实例方法。必须要有对象。
十八、说一下final关键字
- final修饰的变量不能被修改,修饰的类无法继承。
- final修饰的实例变量,比如手动赋值,不能采用系统默认值。
十九、说一下抽象类
- 抽象类是类和类之间有共同特征,将这些具有共同特征的类再进一步抽象形成了抽象类,由于类本身是不存在的,所以抽象类无法创建对象。抽象类还可以进一步抽象。
- 抽象类也属于引用数据类型,抽象类最多是个半抽象。
- 抽象类的定义方式:[修饰符列表] abstract class 类名{ 类体};
- 抽象类是无法实例化的,无法创建对象,所以抽象类是用来被子类继承的。
- final和abstarct是对立的,不能联合使用。
- 抽象类的子类可以是抽象类,抽象类虽然无法实例化,但是抽象类有构造方法,这个方法供子类使用。
- 抽象方法没有方法体,以分号结尾,前面修饰符列表中有abstract关键字,
- 一个非抽象的类继承抽象类,必须将抽象类中的抽象方法实现。
- 抽象类中可以没有抽象方法!,但有抽象方法的一定是抽象类
总结:抽象类其实本身就是一个类,只是可以实现部分抽象方法,这些抽象方法不可以有方法体,并且子类必须实现这个方法。
二十、说一下接口
- 接口也属于引用数据类型,但是!接口是完全抽象的。接口是特殊的抽象类。
- 接口的定义方法:[修饰符列表] interface 接口名{};
- 接口支持多继承,一个接口可以继承多个接口。
- 接口只允许有两部分内容,一部分是常量,一部分是抽象方法。
- 接口中所有的元素都是public修饰的,接口中的抽象方法定义时,public abstract 可以省略
- 接口中的方法都是抽象方法,所以接口中的方法不能有方法体
- 接口中常量的public static final 是可以省略的,接口中的常量不能修改。
二十一、说一下Object类中的toString()方法
- 首先我们要知道,Object类是所有类的父类(String类已经重写了equlas方法),即每个类都直接或间接的继承自该类
- toString默认实现是类名@对象的内存地址转为16进制
- SUN公司为什么要这么设计?为了通过调用toString方法可以将一个java对象转换成字符串。
- 我们也可以对toString进行重写。
二十二、说一下equlas和==的区别
-
equals比较的是两个对象值是否相等(String类已经重写了equlas方法),如果没有被重写,比较的是对象的引用地址是否相同;
-
==用于比较基本数据类型的值是否相等,或比较两个对象的引用地址是否相等;
String helloword= new String("helloword"); String helloword1 = new String("helloword"); System.out.println(helloword.equals(helloword1)); //重写了了,⽐比较的是值,输出结果为true System.out.println(helloword== helloword1); //⽐比较的是引⽤用地址,输出结果为false //⽐比较基本类型的值 int age = 10; int age1 = 10; System.out.println(age == age1); //输出为true
二十三、说一下什么是匿名内部类?
- 再类的内部又定义了一个新的类,称为内部类
- 内部类分为:静态内部类类似于静态变量 ; 局部内部类类似与局部变量 ; 实力内部类类似于实例变量
二十四、分析一下程序,共创建了几个对象。
String s1=new String("hello");
String s2=new String("hello");
方法区字符串常量池有1个'hello'对象,堆内存有两个'hello'对象,共三个
解释:如果创建的是对象,那么栈内存中的引用地址会去堆内存中找对象地址,然后通过堆内存中的地址找到方法区的字符串常量池。
如果是String s3="hello",那就一个对象,即直接根据栈内存的引用地址找方法区的字符串常量池
二十五、详细说一下StringBuffer
-
在java中,字符串长度是不可变的,每一次拼接都会产生新的字符串,会占大量的方法区内存,造成内存空间浪费。
比如String s=“abc”,String s1=“hello”;
实际上方法区常量池中创建了3个对象,“abc”,“hello”,“abchello”(假如我们已经拼接了)
-
StringBuffer底层实际上是一个byte[]数组,往StringBuffer中放字符串,实际上是放到byte[]数组中,StringBuffer初始底量为16
怎么用?
StringBuffer sb=new StringBuffer(); sb.append("");//append拼接字符串,一旦加满16,append会自动扩容
-
说一下StringBuffer和StringBuilder区别:前面有synchronized修饰,即是在多线程环境下运行是安全的。而后面那个没有。
二十六、解释一下什么是装箱什么是拆箱
-
装箱是指将基本数据类型转换为引用数据类型 比如int—>Integer
-
拆箱是指将引用数据类型转换为基本数据类型 比如Integer—>int
常用的转换函数:
int和Integer之间:装箱用Integer.valueof()拆箱用 intValue()
int和String之间:int–>String 用:String.valueof(int) String---->int用:SInteger.parseInt(“123”)
String和Integer之间:String---->Integer 用:Integer.valueof(“123”); Interger---->String用:String.valueof(Integer对象)
二十七、说一下编译时异常和运行时异常的区别
- 编译时异常一般发生概率比较高,比如外面下雨,你看到可能会预料,不打伞可能会生病(生病就是一种异常),所以我们出门前一定要带一把伞(预处理)
- 运行时异常一般发生的概率比较低,比如出门掉钱。或在出门前没必要提前对那些概率低的异常进行预处理。
- 为什么要区分两种异常?如果不区分,所有异常都得预先处理,你这个人会更加安全,但是会很累。
二十八、final、finally、finalize有什么区别?
- final是一个关键字,表示最终得不变的。
- 在finally子句种得代码是最后执行的,并且一定会执行,即使try语句出现了异常,通常用在完成资源的释放、关闭
- finalize()是object得一种方法,由JVM得GC垃圾回收器调用
二十九、什么是集合?有什么用?
-
集合实际上是一个容器,可以来容纳其他类型的数据。
-
集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的是java对象得内存地址(或者说是引用)
比如:list.add(100);//这里并不是基本数据类型,而是Integer,这里它自动装箱了。
三十、集合分为哪辆类?分别介绍一下典型得集合
- 集合分为**单个方式存储元素(java.util.Collection)和以键值对儿(java.util.Map)**的方式存储元素
说一下Iterator中hasNext和Next的区别:hasNext负责看看有没有下一个,而next负责进行下一步并拿出数据。
//Collection中的遍历
Iterator it=list.iterator();
while(it.hasNext()){
Object obj=it.next();
System.out.printf(obj);
}
或
这种方式基本没人用了,stream流、foreach都取代了这种方式
//Map中的遍历
Set<Interger> keys=map.keySet();
Iterator<Integer> it=keys.iterator();
while(it.hasNext){
Integer key=it.next();
String value=map.get(key);
System.out.println(key+"="+value);
}
或
for(Integer key:keys){
System.out.println(key+"="+map.get(key));
}
三十一、说一下泛化机制
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
使用泛化指定某一类型后,只允许集合中存放某一个指定类型,使集合元素更加统一
比如:集合中的泛化
List<Animal> list=new ArrayList<>();//后面那个<>可填可不填,系统自动匹配
三十二、说一下流的四大家族
- java.io.InputStream 字节输入流 (FileInputStream比较常用)
- java.io.OutputStream 字节输出流
java.io.Reader 字符输入流 (BufferedReader比较常用) - java.io.Writer 字符输出流
三十三、说一下什么是序列化和反序列化
序列化:java对象存到文件中,将java对象的状态保存下来的过程
反序列化:将硬盘上的数据重新恢复到内存上,恢复成java对象。
三十四、怎么创建一个多线程?
/*
start():启动一个分支线程,在JVM中开发一个新的栈空间,只是为了开辟栈空间。
调用start后,两个线程是并发的,间隔时间很短,所以可以看作一起运行
run():只运行run,是在主栈中运行,先运行run,在main方法,并没有开辟分支栈。
*/
public class ThreadTest {
public static void main(String[] args) {
//方法一:
// MyThread myThread=new MyThread();
// myThread.run();
//方法二:接口方式,最常用
// MyRunnable r=new MyRunnable();
// Thread t=new Thread(r);
// t.run();
// System.out.println(t.getName());
//方法三:匿名内部类方式
Thread t=new Thread(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("我是来自第三种方法的Thread");
System.out.println(Thread.currentThread().getName());
}
});
t.start();
}
}
//方法一:
class MyThread extends Thread{
@Override
public void run() {
System.out.println(getName());
}
}
//方法二:
class MyRunnable implements Runnable{
//注意:单纯实现接口并不是一个线程类,而是一个可运行的类,还不是一个线程。
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
三十五、在开发中怎么解决线程安全问题?
- 尽量使用局部变量代替”实例变量“和静态变量
- 如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。
- 如果不能使用局部变量,对象也不能创建多个,这时候就只能选择synchronized了。但是效率会下降,且用户体验差。
三十六、什么是反射机制?
- 操作字节码文件,来获取类的属性、方法,简单来说,就是对类的刨析