Bootstrap

JavaSE基础知识笔记

​1、基础语法

  1. 在一个Java源文件中可以声明多个class,但是最多只有一个类可以被声名为public,而且被声名为public的类的类名必须与源文件名相同。

  2. 计算机底层都以补码的方式来存储数据!目的是为了简化计算机的结构设计,同时提升运算速度!

  3. 编译时,会生成一个或者多个字节码文件,字节码文件的文件名字与Java源文件中的类名相同。

  4. Java中针对不同数据类型获取长度的几种方法:

  • Java中的length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了length这个属性.

  • Java中的length()方法是针对字符串String说的,如果想看这个字符串的长度则用到length()这个方法.

  • Java中的size()方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此size()方法来查看。

4. 要注意的细节:数组的索引是字符串类型!

  1. 取模运算符最后的结果与被取模数的符号相同。

  2. 自增或者自减不会改变本身变量的数据类型

  3. 对于赋值运算符,+=、-=、*=等,其不改变变量本身的数据类型,比如char s,当执行s += 3,时,其char还是char的数据类型!对于赋值运算时,更喜欢这种操作!

  4. Java语言的程序流程控制只有三种:顺序结构、分支结构、循环结构

  5. break在switch-case语句中是可选的!且case语句是可以合并的,case语句里边只能是常量(byte、short、char、int、枚举类型(JDK5.0之后新增)、String类型(JDK7.0之后新增)),而不能是其他形式(如boolean判断类型等)!

在编程分支结构时,如果switch-case语句(且case情况不太多)和if-else语句都可以使用时,优先使用switch-case语句!原因:switch-case执行效率稍高!

  1. ==equals()的区别比较:

== 的作用:   基本类型:比较是否相等   引用类型:比较内存地址值是否相等

equals 的作用:   引用类型:默认情况下,比较内存地址值是否相等。可以按照需求逻辑,重写对象的equals方法。

  1. Java语言中在定义一个变量时,最好直接将其赋值,不然如果最后输出这个变量时会产生难以想到的后果(比如这个变量在程序运行时没有被赋值就会编译失败!)

  2. 对于嵌套循环,外侧循环表示的是行,内侧循环表示的是列,写代码时要找到行和列即ij之间的关系!

  3. 如何高效计算2*8?

  • 2<<3 : 2左移3个位置

  • 8<<1:8左移1个位置

  1. if-else语句执行时遵循就近原则

  2. 针对嵌套的双层for循环:(如果可视化的角度分析)

  • 外层循环控制行数

  • 内层循环控制列数

  1. 如果动态初始化一个数组时:int[] [] arr = new int[3] [4],此时如果输入一维数组的值时,其表示的是地址值,如:System.out.println(arr1[0])其表示的是地址。

  2. 匿名对象的概念:在创建对象时,没显式的赋给一个变量名。即为匿名对象。

    特点:匿名对象只能调用一次。

    new Phone().sendEmail();

  3. 怎么理解this关键字?怎么理解super关键字?

this就可以理解为当前的对象,或者正在创建的对象!

比如使用匿名对象时,其造的对象是新的一个对象,不符合自己想要的当前的这个对象,这个时候就可以用this来代之当前对象。

this后面可以跟属性、方法、构造器(this(添加相应的参数),如果这样写的话要放在第一行)

具体点,this调用构造器注意以下几点:

  • 在类的构造器中,可以显式的使用 “this(形参列表)” 方式,调用本类中指定的其他构造器

  • 构造器中不能通过 “this(形参列表)” 方式调用自己

  • 如果一个类中有 n 个构造器,则最多有 n - 1 个构造器中使用了 “this(形参列表)”

  • 规定,“this(形参列表)” 必须声明在当前构造器的首行

  • 构造器内部,最多只能声明一个 "this(形参列表)" ,用于调用其他构造器

super就可以理解为父类的,父类的对象

  • 我们可以在子类的方法或者构造器中,通过使用 ”super.属性“ 或者 ”super.方法“ 的方式,显示的调用父类中声明的属性或者方法,但是,通常情况下,我们习惯省略 "super."

  • 特殊情况下,当子类和父类中定义了同名的属性是,我们要想在子类中调用父类中声明的属性,则必须是显示的使用 "super.属性" 结构,则表明调用的是父类中声明的属性。

  • 特殊情况下,当子类重写了父类中的方法后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用 "super.方法" ,表明是父类中被重写的方法。

  • 与this一样,super也必须声明在首行。

  • 在类的构造器中,针对 "this(形参列表)" 或 "super(形参列表)" 只能二选一,不能同时出现

  • 在构造器首行,如果没有显示的声明 "this(形参列表)" 或 "super(形参列表)" ,则默认的是调用 "super(形参列表)" ,即默认调用的是父类的空参构造器

  • 在类的构造器中,至少有一个类的构造器中使用了 "super(形参列表)" ,调用父类中的构造器

  1. 在某些时候,引用数据类型,你就可以把它理解为一个类,所以在隐式定义引用类型后,需要先new好在使用,否则会报错,例如:private Customer[] customers; 如果不在构造函数中初始化这个对象数组时就会报错,应当在构造函数中加上一句:customers = newCustomer[10];

  2. 一个类中使用其他类作为引用数据类型时,这两个类构成关联关系。

  3. String的底层就是char!

  4. 针对语句while(i-- !=0):先执行判断语句,再执行i--语句。

  5. 一个容易出错的小知识:

     String s1 = "BB";
     String s2 = "BB";
     System.out.println(s1 == s2); //此处为true!!
     ​
     //注意:如果此处为Integer也是同理,常量池的缘故!范围为(-128~127)

    因为String虽然可以理解为是一个类,但它不是放在堆空间中的,而是放在常量池中的,常量池有一个特性就是:如果发现有相同的内容,其会进行复用!!所以此处是true。

  1. 回溯是很经典的一个算法,什么是回溯,回溯其实是一种暴力枚举的方式,为啥都暴力了还是很经典的一种方法呢,其实是因为有些问题我们能暴力出来就不错了,就别要其他自行车了。常见的回溯类问题:组合;排列;切割;子集;棋牌; 其实回溯算法就是常说的DFS,本质上是一种暴力枚举算法; 回溯算法常用于解决的问题: 组合 排列 切割 子集 棋盘

  2. 关于基本数据类型、包装类、String类之间的转化,JDK5.0之后有新特性:

    • 基本数据类型 <--> 包装类 : 自动装箱,自动拆箱

    • 基本数据类型、包装类 --> Sting类 : 调用String.valueOf(xxx)

    • Sring类 --> 基本数据类型、包装类:调用包装类的parserXxx(String str),类如Integer.paserInt(String str)

  3. 三元运算符会自动进行类型提升,运算结果的类型会提升至中间类型和右边类型的最高的那个。

  4. 怎么理解多态

    子类对象赋给父类引用(父类引用指向子类对象),编译看左边,运行看右边!

    Person p = new Man();

    多态使用前提:①存在子父类的继承 ②存在子类重写

    注意:对象的多态性,只适用于方法,不适用于属性(编译运行都看右边)

    多态是一个运行时行为!

  5. 理解向上转型和向下转型:

    向上转型:多态

    向下转型:编译时只调用了父类的属性和方法,子类的属性和方法不能调用,如何调用就用到了向下转型:使用强制类型转换符()! Man m = (Man) p;

    注意:可能会出现ClassCastException的异常,为避免异常,在向下转型时,先进行instanceof的判断,返回true就行了。

    instanceof的使用:a instanceof B : a为对象,B为类,即a时B的实例,当对象是右边类或者子类所创建的对象时,返回true。 与上面对应的话就是,p instanceof Man;

    其次,m instanceof Man/Person; 此时结果均为true,类只要是其父类,结果均是true

  6. 抽象类和接口肯定体现了多态性,因为抽象类和接口不能实例化。

  7. 数组也可以看成是特殊的类。

  8. 单元测试方法:满足无返回值,无形参 ,同时此类为public,此类提供公共无参构造器。

  9. static关键字

    使用static修饰属性,静态变量(或类变量)

    使用static修饰方法:

    注意: 开发中,如何确定一个属性是否要声明为static的?

    属性是可以被多个对象所共享的,不会随着对象的不同而不同 类中的常量也常常声明为staic 开发中,如何确定一个方法是否要声明为static的? 操作静态属性的方法,通常设置为static的 工具类中的方法,习惯上声明为static的。比如。Math、Arrays、collections,因为这样就可以节省了创建对象这个步骤!

    • 属性,按是否使用static修饰,又分为。静态属性 VS 非静态属性(实例变量)

      • 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

      • 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

    • static修饰属性的其他说明:

      • 静态变量随着类的加载而加载。可以通过 "类.静态变量" 的方式进行调用,静态变量的加载要早于对象的创建。

      • 由于类只会加载一次,则静态变量在内存中也只会存在一份。存在方法区的静态域中。

    • 举例:System.out, Math.PI

    • 使用static修饰方法:静态方法

    • 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用

    • 静态方法中,只能调用静态的方法或属性 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性 static注意点:

      • 在静态的方法内,不能使用this关键字、super关键字

      • 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。类的生命周期早于对象的生命周期,即静态方法/属性的生命周期早于非静态方法/属性的生命周期。

  1. final 关键字

    final可以用来修饰的结构。类、方法、变量 final用来修饰一个类:此类不能被其他类所继承。比如:String类、System类、StringBuffer类

    final用来修饰方法:表明此方法不可以被重写 比如:Object类中getClass();

    final用来修饰变量:此时的"变量"就称为是一个常量

    final修饰属性。可以考虑赋值的位置有,显式初始化、代码块中初始化、构造器中初始化 final修饰局部变量: 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦以后,就只能在方法体内使用此形参,但不能进行重新赋值。

    static final:用来修饰属性,全局常量

  2. abstract关键字

    abstract修饰类;抽象类

    此类不能实例化 抽象类中一定有构造器,便于子类实例化时调用(涉及。子类对象实例化的全过程) 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 abstract修饰方法。抽象方法 抽象方法只有方法的声明,没有方法体 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。 若子类重写了父类中的所有的抽象方法后,此子类方可实例化 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

    abstract使用上的注意点; 1.abstract不能用来修饰,属性、构造器等结构 2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类

    抽象方法的理解:是模块使用的必须!

     抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
     解决的问题:
     >当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去(即将不确定的方法调整为抽象方法),让子类去实现(即让子类继承重写父类中不确定功能的抽象方法)。
     >换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽
     象出来,供不同子类实现。这就是一种模板模式。

  3. 关于接口的注意事项

    1.接口中不能定义构造器,意味着接口不可以实例化!

    2.类可以实现多个接口--->这也弥补了Java单继承性的局限性!

    3.接口与接口之间可以继承,而且可以多继承

    4.接口本质上可以看作是一种规范!体现了多态性!

    5.接口中定义的静态方法,只能通过接口来调用!

    通过实现类的对象,可以调用接口中的默认方法!

  4. 关于内部类:

    创建静态的成员内部类:Person.Dog dog = new Person.dog();

    创建非静态的成员内部类:Person p = new Person(); Person.Bird bird = p.new Bird ();

  5. 异常处理:

    两种方式:try-catch-finally 与 throws

    在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 Exception 和 Error。

    • Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。

    • Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。

    针对try-catch-finally 的语句,像数据库连接,输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要写在finally语句中。

    使用try-catch-finally 语句,目的使得程序在编译时不再报错,但是运行时还是有可能报错的。开发时,针对运行时异常,我们不再使用try-catch-finally 语句!

    体会:try-catch-finally:真正的将异常给处理掉了!

    throws的方式只是将异常给了方法的调用者,并没有真正的将异常给处理掉!

    注意:子类重写的方法抛出了异常要 不大于 父类被重写的方法抛出的异常类型!

问题:开发过程中什么时候用throws和try-catch-finally,具体可以看下边

    • 创建多线程的方式有哪些?

      比较经典的:继承Thread类和实现Runnable接口

    • 出现线程安全问题如何解决?

      采用同步,即加锁!

      方式一:同步代码块

       synchronized(同步监视器){  //同步监视器就是锁,任何一个类的对象都可以充当锁,但是多个线程必须共用同一把锁,
       //需要被同步的代码,在类中可以用 大类.class来充当锁。
       }
       ​
    • 死锁问题:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。出现死锁,所有线程没有提示,处于阻塞状态,程序无法继续。

    • 解决:改算法/尽量避免嵌套同步/尽量减少同步资源的定义

2、String、StringBuffer和StringBuilder类的区别

Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。String 类是不可变类,即一旦一个 String 对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。

Java 提供了两个可变字符串类 StringBuffer 和 StringBuilder,中文翻译为“字符串缓冲区”。

StringBuilder 类是 JDK 1.5 新增的类,它也代表可变字符串对象。实际上,StringBuilder 和 StringBuffer 功能基本相似,方法也差不多。不同的是,StringBuffer 是线程安全的,而 StringBuilder 则没有实现线程安全功能,所以性能略高。因此在通常情况下,如果需要创建一个内容可变的字符串对象,则应该优先考虑使用 StringBuilder 类。

StringBuffer、StringBuilder、String 中都实现了 CharSequence 接口。CharSequence 是一个定义字符串操作的接口,它只包括 length()、charAt(int index)、subSequence(int start, int end) 这几个 API。

StringBuffer、StringBuilder、String 对 CharSequence 接口的实现过程不一样,如下图所示:

可见,String 直接实现了 CharSequence 接口,StringBuilder 和 StringBuffer 都是可变的字符序列,它们都继承于 AbstractStringBuilder,实现了 CharSequence 接口。

总结

  • String 是 Java 中基础且重要的类,被声明为 final class,是不可变字符串。因为它的不可变性,所以拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。

  • StringBuffer 就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类。它提供了 append 和 add 方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列。

  • 在很多情况下我们的字符串拼接操作不需要线程安全,所以 StringBuilder 登场了。StringBuilder 是 JDK1.5 发布的,它和 StringBuffer 本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。

线程安全:

StringBuffer:线程安全,执行速度慢 StringBuilder:线程不安全,执行速度快

速度:

一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。

使用环境:

操作少量的数据使用 String。 单线程操作大量数据使用 StringBuilder。 多线程操作大量数据使用 StringBuffer。

3、Map和HashMap区别:

不能单独使用 Map 接口来保存数据,但是我们可以创建其实现类的对象,然后使用 Map 引用来保存对象。在这里,我们使用 HashMap 类来存储数据,并使用 Map 接口来保存该类的引用。请参见下面的示例。

 import java.util.HashMap;
 import java.util.Map;
 ​
 public class SimpleTesting{ 
     public static void main(String[] args) {
         Map<String, Integer> map = new HashMap<>();
         map.put("One", 1);
         map.put("Two", 2);
         map.put("Three", 3);
         System.out.println(map);
     }
 }
输出:

 {One=1, Two=2, Three=3}

HashMapMap 接口的一个实现类。因此,我们可以使用它来创建键值对的集合。请参见下面的示例。

 import java.util.HashMap;
 ​
 public class SimpleTesting{ 
     public static void main(String[] args) {
         HashMap<String, Integer> map = new HashMap<>();
         map.put("One", 1);
         map.put("Two", 2);
         map.put("Three", 3);
         System.out.println(map);
     }
 }
输出:

 {One=1, Two=2, Three=3}

Map接口:

  • Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储,能够实现根据key快速查找value

  • Map中的键值对以Entry类型的对象实例形式存大

  • 键(key值)不可重复——value值可以

HashMap类:

  • HashMap是Map的一个重要实现类,也是最常用的,基于哈希表实现

  • HashMap中的Entry对象是无序排列的

  • Key值和value值都可以为null,但是一个HashMap只能有一个key值为null的映射(key不可重复)

HashMap与Map的关系如图:

4、内存解析结构

栈:放局部变量

堆:new出来的结构,对象、数组

5、Java参数的值传递机制

Java方法里的参数传递机制只有一种:值传递。即将实际参数值的复制品传入方法内,参数本身不受影响。

  • 形参是基本数据类型时,将实参基本数据类型变量的“数据值”复制并传递给形参。当调用结束时参数原来的值不受影响。

  • 形参时引用数据类型时,将实参引用数据类型变量的“地址值”传递给形参。当调用结束时参数之前的值会受影响。

方法参数传递的内存解析例子示意图:

6、方法的重载

定义:在同一个类当中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同和即可。即:同类,同方法名,不同参数列表。

7、方法重载和重写的区别

重载定义

在同一个类当中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同和即可。即:同类,同方法名,不同参数列表。

重写定义(overwrite / override):

子类继承父类之后,可以对父类中的同名同参的方法进行覆盖操作。在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。

规定:

①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同。

②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符

特殊情况,子类不能重写父类中声明为private权限的的方法

③返回值类型

父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void

父类被重写的方法的返回值类型时A类型,则子类重写的方法的返回值类型可以是A类型或者A类得子类。

父类被重写的方法的返回值类型是基本数据类型(例如double),则子类重写的方法的返回值类型相同的类型

④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

注意:子类和父类中的同名同参数的方法要么都声明为非static的,要么都声明为static的。(不叫重写)

8、throws和throw区别

9、Collection和Collections的区别

10、final、finally、finalize区别

11、类封装性的体现

  • 将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)

  • 不会外暴露私有的方法

  • 单例模式

什么是单例模式?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例。

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

  • 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

以下为饿汉式的单例模式

步骤一:创建SingleObject类

 public class SingleObject {
  
    //创建 SingleObject 的一个对象
    private static SingleObject instance = new SingleObject();
  
    //让构造函数为 private,这样该类就不会被实例化!
    private SingleObject(){}
  
    //获取唯一可用的对象
    public static SingleObject getInstance(){
       return instance;
    }
  
    public void showMessage(){
       System.out.println("Hello World!");
    }
 }   

步骤二:从 singleton 类获取唯一的对象。

public class SingletonPatternDemo {
   public static void main(String[] args) {
 
      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();
 
      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();
 
      //显示消息
      object.showMessage();
   }
}
输出结果:Hello World!

以下为懒汉式的单例模式

区分饿汉式和懒汉式

饿汉式:

坏处:对象加载时间过长。好处:饿汉式是线程安全的

懒汉式:

好处:延迟对象的创建。 目前的写法坏处:线程不安全。--->到多线程内容时,再修改

12、构造函数作用是什么?

作用:初始化对象(创建对象、初始化对象结构)

构造器说明:

  • 如果没有显示的定义类的构造器的话,则系统默认提供一个空参构造器

  • 定义构造器的格式,权限修饰符 类名(形参列表){}

  • 一个类中定义的多个构造器,彼此构成重载

  • 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器

  • 一个类当中至少有一个构造器

  • 默认情况下构造器权限修饰符和类的权限修饰符是一样的。

有什么用?

当你需要大批量的写对象的时候,就需要用到构造函数,它可以方便创建多个对象的实例,并且创建的对象可以被标识为特定的类型,可以通过继承扩展代码

构造函数与普通函数相比,有以下4个特点: 1.用new关键字调用,生成实例对象 2.函数内部使用this关键字,来指向即将要生成的实例对象 3.默认不用return返回值(构造函数不需要用return显式返回值,默认会返回this,也就是新的实例对象) 4.函数命名建议首字母大写,用来区分于普通函数(不是命名规范中的,但是建议这么写)

13、四种权限修饰符作用范围

权限大小排队:private < 缺省 < protected < public

private:仅作用于类内部

缺省:作用在同一个包下边

protected:作用在不同包的子类

public:作用在一个工程之下

使用范围:四种权限修饰可以用来修饰类的内部结构:属性、方法、构造器、类内部

注意:但是对于类class的权限修饰只可以用public和default(缺省)

14、构造器是否可以被重写?

不可以!

因为构造函数是不能被继承的,重写要求是子父类之间的,所以不能被重写。但是在同一个类中,构造器是可以被重载的。

构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现。

构造器说明:

  • 如果没有显示的定义类的构造器的话,则系统默认提供一个空参构造器

  • 定义构造器的格式,权限修饰符 类名(形参列表){}

  • 一个类中定义的多个构造器,彼此构成重载

  • 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器

  • 一个类当中至少有一个构造器

15、属性赋值的先后顺序

①默认初始化

②显式初始化

③构造器中赋值

④通过“对象.方法”或者“对象.属性”的方式进行赋值

16、什么是JavaBean?

  • 类是公共的

  • 有一个无参的公共的构造器

  • 有属性,且有对应的get、set方法

17、什么是MVC设计模式

M:model的缩写 V:view的缩写 C:control的缩写

C----->M------>V

|

V

SQL

18、import关键字

  • 一般的 import 关键字是导入某个类,其落脚点是某一个类/接口

  • 但是 import static 落脚点是类中的某一个静态结构(属性/方法)

  • 声明位置在包的位置和类的声明之间

  • 如果需要导入多个结构,则并列写出即可

  • 可以使用 “xxx.*" 的方式,表示导入xxx包中所有结构

  • 如果使用的类或接口是本包下定义的,则可以省略import结构

  • 如果使用的类或接口是java.lang包下定义的,则可以省略import结构

  • 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名(com.xxx.xxx)的方式显示。

  • 使用 ”xxx.*“ 方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显示具体的import结构

  • import static :导入指定类或接口中的静态结构:属性或方法。

19、继承性的理解

  1. 一旦子类A继承了父类B后,就继承来B中所有的属性和方法。特别的父类中声明的private的属性和方法,子类继承父类后,仍认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。

  2. 子类继承父类后,还可以声明自己特有的属性和方法,实现功能的扩展。

  3. 一个类可以被多个子类继承。

  4. 单继承性:一个类只能有一个父类

  5. 子父类时相对的概念

  6. 子类直接继承的父类称:直接父类,间接继承的父类称为:间接父类。

  7. 子类继承父类之后,就获取了直接父类以及间接父类中声明的属性和方法。

20、子类对象的实例化过程

1.从结果上看:(继承性)

子类继承父类之后,就获取了父类中声明的属性和方法

创建子类对象,在堆空间中,就会加载所有的父类中声明的属性

2.从过程上看:

当我们通过类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,继而调用父类的父类的构造器,直到调用了java.lang.Object类中的空参的构造器为止,正因为加载了所有的父类的机构,所以才可以看到内存中有父类中的结构,子类对象才能考虑进行调用。

注意:虽然创建子类对象的时候,调用了父类的构造器,但是自始而终就创建了一个对象,即new的一个子类对象。

21、什么是多态(动态绑定),如何理解多态?

对象的多态性:父类的引用指向子类的对象,即Person p2 = new Man();, Person p2 = new Woman();----在这里多态可以理解为向上转型!

多态的使用:当调用父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用。

总结:编译看左边,执行看右边!

多态性的使用前提:①类的继承要有 ②子类存在对父类方法的重写

注意:对象的多态性只适用于方法,不适用于属性!!!对于属性,执行和编译都看左边!


由于变量声明的为父类类型,导致编译时,只能调用父类中声明的属性和方法(方法实际上调用的是子类重写的方法),子类特有的属性和方法不能调用,那究竟如何调用子类特有的属性和方法呢?

Person p2 = new Man();
Man m2 = (Man)p2;  //前提是p2是类Man的实例化对象,不然会报ClassCastException异常

在这里可以理解为向下转型!

但是前提是p2是类Man的实例化对象,不然会报ClassCastException异常,这里就要用到 instanceof的关键词的使用!

a instanceof A:判断对象a是否是类A的实例,如果是返回True,否则返回False。

一道经典的多态面试题!

22、变量和实例变量的内存解析

23、类中代码块的理解

代码块的作用:用来初始化类、对象 代码块如果有修饰的话,只能使用static.

分类:静态代码块 VS 非静态代码块

静态代码块:

内部可以有输出语句 随着类的加载而执行,而且只执行一次

作用:初始化类的信息 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行

静态代码块的执行要优先于非静态代码块的执行 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

非静态代码块:

内部可以有输出语句

随着对象的创建而执行 每创建一个对象,就执行一次非静态代码块

作用:可以在创建对象时,对对象的属性等进行初始化 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

25、开发过程中如何选择使用 try-catch-finally 还是使用throws?

①如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally的方式处理异常。

②在执行方法a中,先后又调用了另外的几个方法,这几个方法是递进方法执行的。我们建议这几个方法使用throws的方式进行处理,而执行的方法a可以考虑使用try-catch-finally方式进行处理。??

注意补充:throw和throws的区别理解?

  • throw是针对的异常对象的产生,是手动的生成一个异常对象,并抛出(throw)

  • throws是“抓取异常”,可以理解为异常的处理方式,有 try-catch-finally 和 throws 两种方法。

 

 

26.创建多线程的方式有哪些?比较他们区别

  1. 继承于Thread类

    ①创建一个继承于Thread类的子类

    ②重写Thread类中的 run() 方法 --> 将此线程执行的操作声名在 run() 中

    ③创建Thread类子类中的对象

    ④通过此对象调用 start()

  2. 实现Runnable接口

    ①创建一个实现 Runnable 接口的类

    ②实现类去实现 Runnable 中的抽象方法 :run()

    ③创建实现类的对象

    ④将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象

    ⑤通多Thread类的对象调用start() 方法

比较:

开发中,优先选择:实现 Runnable 接口的方式

原因:1. 实现的方式没有类的单继承性的局限性

           2. 实现的方式更适合来处理多个线程有共享数据的情况

联系:public class Thread implements Runnable()

相同点:两种方式都是重写了run() ,将线程执行逻辑声名在 run() 中。

27.浅拷贝和深拷贝有什么区别?

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存(分支)。

  • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(不是分支)

28.看下面结果能过添加成功?

---不能添加成功!!

29.那些排序算法是稳定的?哪些不是稳定的?

稳定的:冒泡、直接插入、归并排序

不稳定:快排、选择排序、希尔排序、计数排序、桶排序

30.Redis 分布式锁是如何实现的呢?

  • Redis 利用setnx实现的分布式锁是不可重入的!

  • Redission 实现的分布式锁是可重入的(前提保证是同一个线程内),这样可以提高高并发场景下分布式锁的有效性!

Redission 实现的分布式锁底层使用的是setnx 和 lua脚本(保证原子性)

---------Redission实现分布式锁,如何控制锁的有效时长??

在Redission的分布式锁中,提供了一个WatchDog(看门狗),一个线程获取锁成功之后,WatchDog会给持有锁的线程续期(默认是每隔十秒续期一次)

---------Redission这个锁能够解决主从数据一致性的问题吗??

不能解决,但是可以用Redission提供的红锁(AP思想,即保证高可用性)来解决,但是这样的话性能太低了,如果业务中非要保证数据一致性问题,可以采用zookeeper(CP思想,即保证高一致性)实现分布式锁!!

;