总结一些JavaSE的基础问题

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了。但是效率会下降,且用户体验差。

三十六、什么是反射机制?

  • 操作字节码文件,来获取类的属性、方法,简单来说,就是对类的刨析
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

于弋gg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值