# 数据类型面试题;

数据类型面试题; (数据类型)

一 . Java中有哪些数据类型?

Java中数据类型分为基本数据类型和引用数据类型2种

基本类型:byte short int long double float char boolean

引用数据类型: 类 接口 数组

byte(默认值0,占1字节)、

short(默认值0,占2字节)、

int(默认值0,占4字节)、long

(默认值0,占8字节)、float

(默认值0.0,占4字节)、

double(默认值0.0,占8字节)、

char(默认值\u0000,占2字节)、

boolean(默认值false)

引用类型:类(默认值null)、接口(默认值null)、数组(默认值null)

二. == 和 equals 的区别是什么?

==操作符比较的是值【变量(栈)内存中存放的对象的(堆)内存地址】,也就是用于比较变量所对应的堆内存中所存储的数值是否相同。

equal用于比较两个对象的值是否相同【不是比地址】

Object类中的equals方法和“==”是一样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”。对于基础数据类型来说,没有重写equals方法,故两者是一样。

1、基础数据类型 使用==和equals进行比较

public class BaseTypeDemo {
    public static void main(String[] args) {
        //对于基本类型的变量。"=="和"equal"的区别
        int t1=57;
        int t2=67;
        int t3=124;
        int t4=124;
        //“==”对于基本数据类型,判断两个变量的值是否相等。
        Boolean result1=(t1==t2);
        Boolean result2=((t1+t2)==t3);
        Boolean result3=(t3==t4);
        System.out.println("【t1==t2】"+result1);
        System.out.println("【(t1+t2)=t3】"+result2 );
        System.out.println("【t3=t4】"+result3);
    //“equal”不能用于基本数据类型。只能用于类变量。对于基本数据类型要用其包装类。
    Integer i1=new Integer(t1);
    Integer i2=new Integer(t2);
    Integer i3=new Integer(t3);
    Integer i4=new Integer(t4);






    Boolean ri1=i1.equals(i2);
    Boolean ri2=i3.equals(i1+i2);
    Boolean ri3=i3.equals(i4);
 
    System.out.println("【i1.equals(i2)】"+ri1);
    System.out.println("【i3.equals(i1+i2)】"+ri2);
    System.out.println("【i3.equals(i4)】"+ri3);
}

}

【t1==t2】false
【(t1+t2)=t3】true
【t3=t4】true
【i1.equals(i2)】false
【i3.equals(i1+i2)】true
【i3.equals(i4)】true
2、String数据类型 使用==和equals对比

public class StringDemo {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = new String("Hello");
        String str3 = str2; // 引用传递
        System.out.println(str1 == str2); // false
        System.out.println(str1 == str3); // false
        System.out.println(str2 == str3); // true
        System.out.println(str1.equals(str2)); // true
        System.out.println(str1.equals(str3)); // true
        System.out.println(str2.equals(str3)); // true
    }
}

3、Object数据类型 使用==和equals对比

如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两 个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。

1)如果没有重写 对象的Equals方法,直接对比,走的是跟 == 一样的方法

@Test
public void testEquals(){
    System.out.println("application ....................");
    User u = new User("xiaozheng","123456");
    User u1 = new User("xiaozheng","123456");
    System.out.println(u.equals(u1));
    System.out.println(u == u1);

}
false
false

这是因为,默认情况下,如果不重写equals方法,默认走的是 == ,比较对象的值是否一样

2)重写 对象的Equals方法:

@Override
public boolean equals(Object obj) {
    //如果内存地址相等,那么一定是同一个对象,就无需比较两个对象的属性值
    if(this==obj){
        return true;
    }
    //判断obj是否为Baboon类型的实例
    if(obj instanceof User){
        User b = (User)obj;//强制转换
        if(!this.username.equals(b.username)){
            return false;
        }else if(!this.password.equals(b.password)){
            return false;
        }
        return true;
    }else{
        return true;
    }
}
@Test
public void testEquals(){
    System.out.println("application ....................");
    User u = new User("xiaozheng","123456");
    User u1 = new User("xiaozheng","123456");
    System.out.println(u.equals(u1));
    System.out.println(u == u1);
true
false


三. double 和float 的区别:

1.double是双精度浮点数,内存占8个字节,有效数字16位,表示范围是-1.79E+ 308~-1.79E+308。

float是单精度浮点数,内存占4个字节,有效数字8位,表示范围是 -3.40E+38~3.40E+38。

2.两者处理速度不同,CPU处理float的速度比处理double快。double的精度高,double消耗内存是float的两倍。

3.如果不声明,小数默认是double类型,用float时需要进行强转,或者在小数后加上f。

4 .String,StringBuffer与StringBuilder的区别??

面试回答:


**String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

速度:StringBuilder > StringBuffer > String 

   因为StringBuilder快,在不考虑线程安全安全的情况下,优先使用;
   

**

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于

生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会

对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象

引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符

串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下

的字符串对象生成中, String 效率是远要比 StringBuffer 快的:

 String S1 = “This is only a” + “ simple” + “ test”; StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:String S2 = “This is only a”;String S3 = “ simple”;String S4 = “ test”;String S1 = S2 +S3 + S4;这时候 JVM 会规规矩矩的按照原来的方式去做

在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。

5. String str=“donkey” 与 String str=new String(“donkey”) 一样吗?

分配地址: 不一样,因为内存的分配方式不一样。String str=“donkey”,java 虚拟机会将其分配到常量池中;而 String str=new String(“donkey”) 则会被分到堆内存中。

6 .字符串如何反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法

StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("abcdefg");System.out.println(stringBuffer.reverse()); // gfedcbaStringBuilder stringBuilder = new StringBuilder();stringBuilder.append("abcdefg");System.out.println(stringBuilder.reverse()); // gfedcba

7. String 类中常用方法都有哪些?

1. String str = " app le ";   // indexOf(): 返回指定字符的索引      System.out.println(str.indexOf("a")); // 1   // charAt(): 返回指定索引处的字符      System.out.println(str.charAt(5)); // l   // replace(): 字符串替换      System.out.println(str.replace("pp", "cc")); // " acc le "   // trim(): 去除字符串两端空白      System.out.println(str.trim()); // "app le"   // split(): 分割字符串,返回一个分割后的字符串数组      String[] arr = str.split(" ");      // getBytes(): 返回字符串的 byte 类型数组   byte[] bytes = str.getBytes();      // length(): 返回字符串长度   System.out.println(str.length()); // 8      // toLowerCase(): 将字符串转成小写字母   System.out.println(str.toLowerCase()); // " app le "      // toUpperCase(): 将字符串转成大写字符   System.out.println(str.toUpperCase()); // " APP LE "      // substring(): 截取字符串   System.out.println(str.substring(2)); // "pp le "      // equals(): 字符串比较   System.out.println("apple".equals(str)); // false

8 . 常量池问题 : 常量池分类

常量池大体可以分为:静态常量池,运行时常量池。静态常量池 :存在于class文件中,比如经常使用的javap -verbose中,常量池总是在最前面运行时常量池:就是在class文件被加载进了内存之后,常量池保存在了方法区中,通常说的常量池 值的是运行时常量池。所以呢,讨论的都是运行时常量池

字符串常量池
最最最流行的、最典型的就是字符串了

典型范例:

String a = "abc";String b = new String("abc");System.out.println(a == b);----*----结果:false
对象储存在堆中,这个是不用质疑的,而a作为字面量一开始储存在了class文件中,之后运行期,转存至方法区中。它们两个就不是同一个地方存储的。

实例

 String s1 = "Hello";    String s2 = "Hello";    String s3 = "Hel" + "lo";    String s4 = "Hel" + new String("lo");    String s5 = new String("Hello");    String s6 = s5.intern(); //这个方法首先在常量池中查找是否存在一份equal相等的字符串如果有的话就返回该字符串的引用,    没有的话就将它加入到字符串常量池中,所以存在于class中的常量池并非固定不变的,可以用intern方法加入新的.    String s7 = "H";    String s8 = "ello";    String s9 = s7 + s8;System.out.println(s1 == s2);  // trueSystem.out.println(s1 == s3);  // trueSystem.out.println(s1 == s4);  // falseSystem.out.println(s1 == s9);  // falseSystem.out.println(s4 == s5);  // falseSystem.out.println(s1 == s6);  // true

1、s1 = = s2 很容易可以判断出来。s1 和 s2 都指向了方法区常量池中的Hello。
2、s1 = = s3 这里要注意一下,因为做+号的时候,会进行优化,自动生成Hello赋值给s3,所以也是true
3、s1 = = s4 s4是分别用了常量池中的字符串和存放对象的堆中的字符串,做+的时候会进行动态调用,最后生成的仍然是一个String对象存放在堆中。

4、s1 = = s9 在JAVA9中,因为用的是动态调用,所以返回的是一个新的String对象。所以s9和s4,s5这三者都不是指向同一块内存

5、s1 = = s6 为啥s1 和 s6地址相等呢? 归功于intern方法,这个方法首先在常量池中查找是否存在一份equal相等的字符串如果有的话就返回该字符串的引用,没有的话就将它加入到字符串常量池中,所以存在于class中的常量池并非固定不变的,可以用intern方法加入新的

需要注意的特例

1、常量拼接
   public static final String a = "123";        public static final String b = "456";        public static void main(String[] args)        {            String c = "123456";            String d = a + b;            System.out.println(c == d); //ture         }        //我们可以发现,对于final类型的常量它们已经在编译中被确定下来,自动执行了+号,把它们拼接了起来,所以就相当于直接”123” + “456”;

2、static 静态代码块

public static final String a;public static final String b;static {    a = "123";    b = "456";}public static void main(String[] args){    String c = "123456";    String d = a + b;    System.out.println(c == d); //flase }

上个例子是在编译期间,就已经确定了a和b,但是在这段代码中,编译期static不执行的,a和b的值是未知的,static代码块,在初始化的时候被执行,初始化属于类加载的一部分,属于运行期。看看反编译的结果,很明显使用的是indy指令,动态调用返回String类型对象。一个在堆中一个在方法区常量池中,自然是不一样的。

9 . 包装类的常量池技术(缓存)

简单介绍
相信学过java的同学都知道自动装箱和自动拆箱,自动装箱常见的就是valueOf这个方法,自动拆箱就是intValue方法。在它们的源码中有一段神秘的代码值得我们好好看看。除了两个包装类Long和Double 没有实现这个缓存技术,其它的包装类均实现了它。

源码:

public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)    return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}


​ private static class IntegerCache {
​ static final int low = -128;
​ static final int high;
​ static final Integer cache[];

​ static {
​ // high value may be configured by property
​ int h = 127;
​ String integerCacheHighPropValue =
​ VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”);
​ if (integerCacheHighPropValue != null) {
​ try {
​ int i = parseInt(integerCacheHighPropValue);
​ i = Math.max(i, 127);
​ // Maximum array size is Integer.MAX_VALUE
​ h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
​ } catch( NumberFormatException nfe) {
​ // If the property cannot be parsed into an int, ignore it.
​ }
​ }
​ high = h;cache = new Integer[(high - low) + 1];
​ int j = low;
​ for(int k = 0; k < cache.length; k++)
​ cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}
分析:我们可以看到从-128~127的数全部被自动加入到了常量池里面,意味着这个段的数使用的常量值的地址都是一样的。一个简单的实例Integer i1 = 40;Integer i2 = 40;Double i3 = 40.0;Double i4 = 40.0;System.out.println("i1=i2   " + (i1 == i2));System.out.println("i3=i4   " + (i3 == i4));-----结果----truefalse

源码原理

原理如下:1、== 这个运算在不出现算数运算符的情况下 不会自动拆箱,所以i1 和 i 2它们不是数值进行的比较,仍然是比较地址是否指向同一块内存2、它们都在常量池中存储着,类似于这样3、编译阶段已经将代码转变成了调用valueOf方法,使用的是常量池,如果超过了范围则创建新的对象
复杂实例[-128~127]  Integer i1 = 40;  Integer i2 = 40;  Integer i3 = 0;  Integer i4 = new Integer(40);  Integer i5 = new Integer(40);  Integer i6 = new Integer(0);  System.out.println("i1=i2   " + (i1 == i2));  System.out.println("i1=i2+i3   " + (i1 == i2 + i3));  System.out.println("i1=i4   " + (i1 == i4));  System.out.println("i4=i5   " + (i4 == i5));  System.out.println("i4=i5+i6   " + (i4 == i5 + i6));  System.out.println("40=i5+i6   " + (40 == i5 + i6));----结果----(1)i1=i2   true(2)i1=i2+i3   true(3)i1=i4   false(4)i4=i5   false(5)i4=i5+i6   true(6)40=i5+i6   true

它们的内存分布大概如下

注意点
1、当出现运算符的时候,Integer不可能直接用来运算,所以会进行一次拆箱成为基本数字进行比较

2、==这个符号,既可以比较普通基本类型,也可以比较内存地址看比较的是什么了

分析:
(1)号成立不用多说
(2)号成立是因为运算符自动拆箱
(3)(4)号是因为内存地址不同
(5)(6)号都是自动拆箱的结果

PS:equals方法比较的时候不会处理数据之间的转型,比如Double类型和Integer类型。

超过范围
假设一下,如果超出了这个范围之后呢?正如前文所言,所有的都将成为新的对象

  Integer i1 = 400;  Integer i2 = 400;  Integer i3 = 0;  Integer i4 = new Integer(400);  Integer i5 = new Integer(400);  Integer i6 = new Integer(0);  Integer i7 = 1;  Integer i8 = 2;  Integer i9 = 3;  System.out.println("i1=i2   " + (i1 == i2));  System.out.println("i1=i2+i3   " + (i1 == i2 + i3));  System.out.println("i1=i4   " + (i1 == i4));  System.out.println("i4=i5   " + (i4 == i5));  System.out.println("i4=i5+i6   " + (i4 == i5 + i6));     System.out.println("400=i5+i6   " + (400 == i5 + i6));----结果----i1=i2   falsei1=i2+i3   truei1=i4   falsei4=i5   falsei4=i5+i6   true400=i5+i6   true

10 . 堆栈空间分配

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

2、堆栈缓存方式

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。

堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

3、效率比较

栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

4、存储内容

栈: 在函数调用时,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向函数的返回地址,也就是主函数中的下一条指令的地址,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

11.String str="i"与 String str=new String(“i”)一样吗?

不一样

因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”)方式,则会被分到堆内存中。

 String str1 = "i";	String str2 = "i";	String str3 = new String("i");	System.out.println(str1 == str2);//ture	System.out.println(str2 == str3);//false

12. 基本数据类型和引用数据类型的区别:

1. 基本数据类型的值是不可变的
任何方法都无法改变一个基本类型的值

2. 基本数据类型不可以添加属性和方法

3. 基本数据类型的赋值是简单赋值

4. 基本数据类型的比较是值的比较

5. 基本数据类型是存放在栈区的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Co3yptbA-1629458232562)(D:\typro _java 笔记\java 基础\Java基础图片\20201115103846794.png)]

1. 引用类型的值是可以改变的

2. 引用类型可以添加属性和方法

3. 引用类型的赋值是对象引用

4. 引用类型的比较是引用的比较

5. 引用类型是同时保存在栈区和堆区中的

13 . 用最有效率的方法计算2乘以8?

2 << 3。

进阶:通常情况下,可以认为位运算是性能最高的。但是,其实编译器现在已经“非常聪明了”,很多指令编译器都能自己做优化。所以在实际实用中,我们无需特意去追求实用位运算,这样不仅会导致代码可读性很差,而且某些自作聪明的优化反而会误导编译器,使得编译器无法进行更好的优化。

14 .String 是 Java 基本数据类型吗?

答:不是。Java 中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type)。

基本数据类型:数据直接存储在栈上

引用数据类型区别:数据存储在堆上,栈上只存储引用地址

15、两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
不对。hashCode() 和 equals() 之间的关系如下:

当有 a.equals(b) == true 时,则 a.hashCode() == b.hashCode() 必然成立,

反过来,当 a.hashCode() == b.hashCode() 时,a.equals(b) 不一定为 true。

15、&和&&的区别?

&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。

&:逻辑与运算符、按位与运算符。

按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。

逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。

16 .Java 有没有goto?

goto是java语言中的保留字,目前还没有在java中使用

goto是c语言中的与条件配合使用,用于跳出循环等操作,java是结构化程序语言,在java中使用goto会造成许多不必要的麻烦,goto还没被使用,java要求简单方便

17.return continue break的区别

总结:
return : 作用于方法,结束当前方法,主要用来返回方法返回值,当方法有返回值的时候,返回对应类型的返回
值,没有返回值时,可以返回空,或者不返回
continue : 作用于语法结构,结束当前方法,结束当前结构,主要用于循环的加速
break : 作用于语法结构,作用于结构结束当前结构,主要多用于循环和switch结构中

这里说一下,我上面说的结构,是指循环体结构结构,switch结构等,不知道怎么形容了这种东西了,哈哈,感觉语法结构咋一听也不知道是什么,解释一下哈

理解好这个三个关键字,使用这三个关键字可以提高代码的执行效率。

5.java 中的 Math.round(-1.5) 等于多少?

math类中提供了三个与取整有关的方法:ceil,floor,round,这些方法的作用与它们的英文名称的含义相对应。
例如:

ceil的英文意义是天花板,该方法就表示向上取整,math.ceil(11.3)的结果为12,math.ceil(-11.6)的结果为-11;
floor的英文是地板,该方法就表示向下取整,math.floor(11.6)的结果是11,math.floor(-11.4)的结果-12;
最难掌握的是round方法,他表示“四舍五入”,算法为math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,math.round(11.5)的结果是12,math.round(-11.5)的结果为-11.

Java中的修饰符:

一、 类修饰符
1.访问修饰符:公共类修饰符public

公共类修饰符 public : Java 语言中类的访问控制符只有 public 即公共的。每个 Java 程序的有且只有一个类是 public,它被称为主类 ,其他外部类无访问控制修饰符,具有包访问性。注意:一个类的内部类可以被其他访问控制修饰符protected、缺省默认(default、friendly)、private修饰,相当于类的成员。

注意:Java类或属性如果缺省访问控制修饰符,就属于default/friendly类型修饰符,但是实际上Java中并没有名为default或者friendly的访问修饰符(即不能使用default或者friendly定义类或变量),只是为了方便标识缺省访问控制符的情况。

2.非访问控制符:抽象类修饰符 abstract 、最终类修饰符 final

(1)抽象类修饰符 abstract :用 abstract 修饰符修饰的类,被称为抽象类。(2)最终类修饰符 final :当一个类不能被继承时可用修饰符 final修饰为最终类。被定义为 final 的类通常是一些有固定作用、用来完成某种标准功能的类。

(3)类缺省访问控制符:如果一个类没有访问控制符,说明它具有缺省的访问控制符特性。此时,这个类只能被同一个包中的类访问或引用。这一访问特性又称为包访问性。

二、方法修饰符
1.访问控制修饰符

公共访问控制符public、保护访问控制符protected、缺省默认访问控制符、私有访问控制符private

2.非访问控制修饰符

 抽象方法控制符abstract 、静态方法控制符static 、最终方法控制符final 、本地方法控制符native 、同步方法控制符synchronized

(1)抽象方法控制符 abstract : abstract 修饰的方法称为抽象方法。抽象方法仅有方法头,没有方法体和具体实现。

(2)静态方法控制符 static :用修饰符 static 修饰的方法称为静态方法。静态方法是属于整个类的类方法;而不使用static 修饰、限定的方法是属于某个具体类对象的方法。 由于 static方法是属于整个类的,所以它不能操纵和处理属于某个对象的成员变量,而只能处理属于整个类的成员变量,即 static 方法只能处理 static的域。

(3)最终方法控制符 final :用修饰符 final修饰的方法称为最终方法。最终方法是功能和内部语句不能更改的方法,即最终方法不能重写覆盖。final固定了方法所具有的功能和操作,防止当前类的子类对父类关键方法的错误定义,保证了程序的安全性和正确性。所有被 private 修饰符限定为私有的方法,以及所有包含在 final 类 ( 最终类) 中的方法,都被认为是最终方法。

(4)本地方法控制符 native :用修饰符 native 修饰的方法称为本地方法。为了提高程序的运行速度,需要用其它的高级语言书写程序的方法体,那么该方法可定义为本地方法用修饰符 native 来修饰。

(5)同步方法控制符 synchronized :该修饰符主要用于多线程程序中的协调和同步。

 关于关键字synchronized更多细节请参考本人另两篇文章: Java同步锁synchronized的最全总结:https://blog.csdn.net/u012723673/article/details/102622537 Java中synchronized关键字的实现原理:https://blog.csdn.net/u012723673/article/details/102681942

三、变量修饰符
1.访问控制符

公共访问控制符public 、保护访问控制符protected 、缺省默认访问控制符、私有访问控制符private。

(1)公共访问控制符 public:用 public 修饰的域称为公共域。由于 public 修饰符会降低运行的安全性和数据的封装性,所以一般应减少 public 域的使用。

(2)私有访问控制符 private:private 修饰的成员变量只能被该类自身所访问,不能被其它任何类 ( 包括子类 ) 访问。

(3)保护访问控制符 protected:用 protected 修饰的成员变量可以被三种类所引用:①该类自身;②同一个包中的其它类;③其它包中的子类。使用修饰符 protected 的主要作用是允许其它包中的子类来访问父类的特定属性。

(4)缺省默认修饰符 :没有访问控制符或者是用default修饰的成员变量可以被该类本身或同一包中的其他类访问。

2.非访问控制符

静态域修饰符static 、最终域修饰符 final 、易失 ( 共享 ) 域修饰符volatile 、暂时性域修饰符transient。

(1)静态域修饰符 static :用 static修饰的成员变量仅属于类的变量,而不属于任何一个具体的对象,静态成员变量的值是保存在类的内存区域的公共存储单元,而不是保存在某一个对象的内存区间。该类的任一对象访问它时取到的都是相同的数据;该类的任一对象修改它时 , 也都是对同一个内存单元进行操作。

(2)最终域修饰符 final :最终域修饰符 final 是用来定义常量的。一个类的域 ( 成员变量 ) 如果被修饰符 final 说明,则它的取值在程序的整个执行过程中都是不变的。

关于修饰符final 更多细节请参考:https://blog.csdn.net/u012723673/article/details/80580011

(3)易失 ( 共享 ) 域修饰符 volatile :易失 ( 共享 ) 域修饰符 volatile是用来说明这个成员变量可能被几个线程所控制和修改。也就是说在程序运行过程中,这个成员变量有可能被其它的程序影响或改变它的取值。通常 volatile 用来修饰接受外部输入的域。

关于修饰符volatile更多细节请参考:https://blog.csdn.net/u012723673/article/details/80682208

(4)暂时性域修饰符 transient :暂时性域修饰符 transient 用来定义一个暂时性变量。其特点是:用修饰符transient 限定的暂时性变量,将指定 Java虚拟机认定该暂时性变量不属于永久状态,以实现不同对象的存档功能。否则,类中所有变量都是对象的永久状态的一部分,存储对象时必须同时保存这些变量。

关于修饰符transient更多细节请参考: https://blog.csdn.net/u012723673/article/details/80699029

四、访问控制修饰符总结
访问级别 访问控制修饰符 同类 同包 子类(不同包) 不同包(其他类)
公共 public 允许 允许 允许 允许
受保护 protected 允许 允许
允许

不允许
默认 缺省修饰符 允许 允许 不允许 不允许
私有 private 允许 不允许 不允许 不允许

注意:protected修饰的属性或方法,允许不同包的子类中访问。注意这里的访问方式是通过继承访问父类中的protected属性或方法,而不是直接通过父类实例访问protected属性或方法

public class Animal{    protected String name;    public int age;}

不同包的子类public class Cat extends Animal{    public void print(){        /*********通过父类直接访问*********/        Animal a = new Animal();        System.out.println(a.name);   //不允许        System.out.println(a.age);   //允许        /*********通过继承访问*********/        System.out.println(this.name);   //允许        System.out.println(this.age);   //允许    } }

18 .抽象类能使用 final 修饰吗?

答案:不能

这个题目主要就是考察抽象类和final修饰符的特性。

抽象类的就是要子类继承然后实现内部方法的。但是final修饰的类是不能再被继承和修改的。所以不能用final修饰。

19 .

19. java中final关键字

1.final表示最终的,不可变的。

  1. final修饰的类(无法被继承)(String )

  2. final修饰的方法(无法被覆盖,重写)

4 , final修饰的局部变量(只能赋一次值)

  public class FinalTest01{    public static void main(String[]args){        //局部变量        int i=100;        //重新赋值, 没问题        i=200;// 再定义一个局部变量     final int k=100;    //重新赋值    //错误: 无法为最终变量k分配值   // k=300;}
final修饰的实例变量(必须手动赋值)

一般情况下
实例变量如果还没有赋值的话,系统会赋默认值

final 修饰实例变量:
系统不负责赋默认值,要求程序员必须手动赋值,只能赋一次,
这个手动赋值,在变量后面赋值可以,在构造方法中赋值也可以

final修饰的实例变量一般添加static修饰 变成静态的,存储在方法区,节省空间。

结论:static final联合修饰的变量成为“常量”
常量名要全部大写,每个单词之间采用下划线衔接****

 常量和静态变量,都是存储在方法区,并且都是在类加载时初始化  常量一般都是公开的:public的。

final修饰引用(只能指向一个对象)

20 .关键字private

Java中引入private的类型,目的是为了防止类中的数据成员,在类的定义之外被修改。也就说,private类型的数据成员只能在定义的时候修改,其余任何地方都不可以,该类的对象也不可以。

而且,private类型的数据可以作为构造函数的输入。不过,我们也可以在类中定义读取和修改private类型数据的public函数。如下程序:

封装性在Java中的体现:
1. 方法是一种封装
2. 关键字private也是一种封装

封装就是将一些细节信息隐藏起来,对外界不可见

我的理解是,在为某个对象赋值或者得到某个对象的值的时候,我们不能直接调用了,需要用成员方法来赋值或者获取

21 .关键字 static

1、static的用法:修饰 变量

    static可以用来修饰成员变量、成员方法以及代码块等,被static关键字修饰的成员都会具备一些特殊属性。

2、static修饰成员变量:

    被static修饰的成员变量叫做静态变量。静态变量具有以下两个特征:       a.静态变量的数据被本类中所有实例对象所共享;       b.如果该静态变量的访问权限高于private,则该静态变量可通过“类名.变量名”直接访问。

访问权限:public>protected>default(一般省略)>private

3、static修饰成员方法:
 在类中,被static修饰的方法称作静态方法。同静态变量一样,如果该静态方法的访问权限高于private,则该静态方法可通过“类名.方法名”直接调用,而不需要创建对象调用。



  需要注意的是,静态方法只能访问被static修饰的成员,其原因就在于静态方法不需要创建对象就可以被调用。

4、static修饰代码块:

在java中,被static修饰的代码块被称作静态代码块。静态代码块在类被加载时,就会被执行,并且只会执行一次(类只会加载一次)。详见实例:

根据输出结果,静态代码块仅被执行一次,并且在类加载时就开始执行,优先于类中的方法。

23 .static 可以修饰类吗?

普通类是不允许声明为静态的,只有内部类才可以。被static修饰的内部类可以直接作为一个普通类来使用,而不需实例一个外部类(见如下代码)在调用的时候,不需要

public class OuterClass {     public static class InnerClass{         InnerClass(){             System.out.println("============= 我是一个内部类'InnerClass' =============");         }     } } public class TestStaticClass {     public static void main(String[] args) {         // 不需要new一个OutClass         new OuterClass.InnerClass();     } }

24.Java内部类用途和用法

一、为什么需要内部类?

java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这
样做可以完成所有你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就行了吗?
的确,以此作为设计内部类的理由,实在没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题 没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

二、内部类的基本定义

在类的内部可以定义成员变量和方法,而且在类的内部也可以定义另一个类,如果类Outer的内部在定义一个类Inner,此时Inner就成为内部类,而Outer则称为外部类。
内部类可以声明为public或private。当内部类声明为public或private,对其访问权限于成员变量和成员方法完全相同。

代码如下:

package com.se.innerclass;class Outer {    private String info = "我爱你中国";    class Inner{        public void print() {            System.out.println(info);        }    }    public void fun() {        new Inner().print();    }}public class InnerClassDemo1 {    public static void main(String[] args) {        Outer outer = new Outer();        outer.fun();    }}

输出结果:

以上的程序中可以清楚的发现,Inner类作为Outer类的内部类存在,并且外部类的fun()方法中直接实例化内部类的对象调用方法print(),但是从代码中可以明显发现,内部类的存在实际上破坏了一个类的基本结构,因为类是由属性及方法组成的,所以这是内部类的一个缺点,那么内部类有哪些优点呢,如果把内部类拿到外面来就能发现内部类的优点了。
如下面的代码:

package com.se.innerclass;class Outer {    private String info = "我爱你中国";    public void fun() {        new Inner(this).print();    }    public String getInfo(){        return info;    }}class Inner{    private Outer outer;    public Inner(Outer outer){        this.outer = outer;    }    public void print() {        System.out.println(outer.getInfo());    }}public class InnerClassDemo2 {    public static void main(String[] args) {        Outer outer = new Outer();        outer.fun();    }}

以上程序完成了内部类同样的功能,但是但是代码明显比之前的更加复杂,所以内部类的唯一好处就是可以方便的访问外部类的私有属性。

三、使用static定义外部类

使用static可以声明属性或方法,而使用static也可以声明内部类,用static声明内部类变成了外部类,但是用static声明的内部类不能访问非static的外部类属性。

package com.se.innerclass;class Outer{    private static String info = "我爱你中国";    static class Inner{        public void print() {            System.out.println(info);        }    }}public class InnerClassDemo3{    public static void main(String[] args) {        new Outer.Inner().print();    }}

以上程序将info属性定义成了static类型,这样程序中就可以通过static声明的内部类直接访问此static属性,当然,如果此时info属性不是static类型,则编译时将出现以下错误:

四、在外部类访问内部类

一个内部类除了可以通过外部类访问,也可以直接在其他类当中调用,调用的基本格式为:
外部类.内部类 内部类对象 = 外部类实例.new 内部类()
以上的操作格式中,首先要找到外部类的实例化对象之后才可以通过外部类的实例化对象去实例化内部类对象。
这里我们可以观察到编译之后的内部类的.class文件。
内部类定义之后,生成的.class文件是以Outer I n n e r 的 形 式 存 在 的 , 在 J a v a 中 只 要 文 件 存 在 Inner的形式存在的,在Java中只要文件存在 InnerJava,则在程序中应将其替换为”.”。
在外部访问内部类代码如下:

package com.se.innerclass;class Outer{    private String info = "我爱你中国";    class Inner{        public void print() {            System.out.println(info);        }    }}public class InnerClassDemo4 {    public static void main(String[] args) {        Outer outer = new Outer();        Outer.Inner inner = outer.new Inner();        inner.print();    }}

五、在方法中定义内部类

也可以在方法类定义一个内部类,但是在方法中定义的内部类不能直接访问方法中的参数,如果方法中的参数要想被内部类所访问,则参数前必须加上final关键字。
在方法中定义内部类:

package com.se.innerclass;class Outer{    private String info = "我爱你中国";    public void fun(final String temp){        class Inner{            public void print(){                System.out.println(info);                System.out.println(temp);            }        }        new Inner().print();    }}public class InnerClassDemo5 {    public static void main(String[] args) {        Outer outer = new Outer();        outer.fun("123");    }}

六、匿名内部类
在java中处理内部类之外,还有一种匿名内部类。匿名内部类就是指没有一个具体名称的类,此概念是在接口和抽象类的应用上发展起来的,那么匿名内部类有哪些作用呢?
一个简单的操作:

  • package com.se.innerclass; /**  * 普通实现
    
  • @author wzy
    *
    */
    interface A {
    public void printInfo();
    }
    class B implements A{
    @Override
    public void printInfo() {
    System.out.println(“Hello world!!!”);
    }
    }
    class X{
    public void fun1(){
    this.fun2(new B());
    }
    public void fun2(A a){
    a.printInfo();
    }
    }
    public class NoInnerClassDemo6 {
    public static void main(String[] args) {
    new X().fun1();
    }
    }

通过以上的方法可以实现相应的功能,但是现在如果接口实现类只使用一次,那么还有必要单独定义一个子类B吗?很显然是没有必要的,所以此时就可以使用匿名内部类完成,代码修改如下:

package com.se.innerclass;
interface A{
public void printInfo();
}
class X{
public void fun1() {
this.fun2(new A(){
@Override
public void printInfo() {
System.out.println(“hello world”);
}});
}
public void fun2(A a) {
a.printInfo();
}
}
public class NoInnerClassDemo7 {
public static void main(String[] args) {
new X().fun1();
}
}

根据Sun建议的良好的编程实践,实例变量应该总是被声明为私有的。当类变量被用作常量的时候,被声明为public是可以的。## 23.变量:当JVM将类定义加载到内存中时,会为静态字段分配内存空间。静态变量只在执行时被初始化一次。在任何的实例变量中初始化之前初始化所有的静态字段。静态字段可以直接通过类名来访问,不需要任何对象引用。## 24.静态方法静态方法通过类引用调用。可以使用对象引用来调用静态方法,但通常这不被认为是一种良好的风格。静态方法中不能有this和super关键字。静态方法可以访问类的静态字段和方法。静态方法不能访问类的非静态字段和方法。静态方法不能被子类重写。## 25.静态初始化器.JVM将静态初始化器的大小限制为64K。因此,不能在静态初始化器中放太多代码。不能从静态初始化器中抛出被检查的异常。不能在静态初始化器中使用this关键字,因为目前还没有创建实例。不能再静态初始化器中调用super。静态初始化器没有返回类型。## 26.接口不能对接口方法使用一下修饰符:private,protected, transient, volatile和synchronized接口与类非常相似,但接口只可以有隐式为公有的和静态的字段,以及隐式为公有的和抽象方法的声明。接口编译成.class文件并且被与加载类相同的线程加载。可以创建类型是接口名字的引用变量,通过这个引用,只有在接口中的方法是可见的。接口中定义的任何常量在类的代码中可以不用前缀访问,因为实现接口使得他们成为实现类的一部分。## 27.抽象类抽象类不能被实例化,但是可以被子类化。接口只包含方法签名,而抽象类可能有许多方法的实现。接口的所有方法默认都是公有的,不能为接口中声明的方法应用其他任何访问修饰符。在抽象类中,实现的方法可以在他们的声明中应用访问修饰符。为了这样做,接口的方法必须是公有的,声明它们为protected或private将会导致错误。在抽象类中,可以为实现的方法应用protected修饰符,但是不能应用private修饰符。接口可以扩展多个接口,抽象类不能从超过一个的抽象类扩展。接口中的所有方法都是隐式抽象的,抽象类可以有一些具体的方法。接口不能有构造函数,抽象类可以声明构造函数。## 23.类加载的过程序一个Java文件从编码完成到最终执行,一般主要包括两个过程编译运行

编译,即把我们写好的java文件,通过javac命令编译成字节码,也就是我们常说的.class文件。

运行,则是把编译声称的.class文件交给Java虚拟机(JVM)执行。

而我们所说的类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。

举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。

由此可见,JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。

**类加载****类加载的过程主要分为三个部分:****加载****链接****初始化****而链接又可以细分为三个小部分:****验证****准备****解析**加载简单来说,加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。### 这里有两个重点:

字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译

类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。

## 注:为什么会有自定义类加载器?一方面是由于java代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。另一方面也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。验证

主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。

包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?

对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?

对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。

对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(private,public等)是否可

被当前类访问?

准备

主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。

特别需要注意,初值,不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型的默认初始值。

比如8种基本类型的初值,默认为0;引用类型的初值则为null;常量的初值即为代码中设置的值,final static tmp = 456, 那么该阶段tmp的初值就是456

解析

将常量池内的符号引用替换为直接引用的过程。

两个重点:

符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。

直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量

举个例子来说,现在调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。

在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。

初始化

这个阶段主要是对类变量初始化,是执行类构造器的过程。

换句话说,只对static修饰的变量或语句进行初始化。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

总结

**相关扩展知识点:****Java虚拟机的基本机构?****什么是类加载器?****简单谈一下类加载的双亲委托机制?****普通Java类的类加载过程和Tomcat的类加载过程是否一样?区别在哪?****简单谈一下Java堆的垃圾回收机制?**## **26. Java虚拟机的基本机构?**不同的java虚拟机,执行引擎是非常不同的:①:最简单的执行引擎是一次执行字节码。②:更快的引擎,更消耗内存,叫做叫即时编译器。③:自适应优化器④:由硬件芯片构成,它用本地方法执行字java节码,这种执行引擎实际上是内嵌在芯片里的。2:第一次执行的字节码会被编译成本地机器码,且被缓存,当方法被调用时可以重用。3.虚拟机开始的时候开始解释字节码,程序运行时记录其使用频繁的代码段,并且编译成本地代码java中有两种方法:java方法和本地方法。java方法是由java语言编写的,编译成字节码,存储在class文件中。本地方法由其它语言(c,c++,汇编语言编写的),编译成和处理器相关的机器代码。<img src="D:\typro _java  笔记\java 基础\Java基础图片\20200129173917804.png" alt="20200129173917804"  />## 1、面向对象的三个基本特征?面向对象的三个基本特征是:封装、继承和多态。继承:让某个类型的对象获得另一个类型的对象的属性的方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过外公开的接口。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。举个简单的例子:英雄联盟里面我们按下 Q 键这个动作:对于亚索,就是斩钢闪对于提莫,就是致盲吹箭对于剑圣,就是阿尔法突袭同一个事件发生在不同的对象上会产生不同的结果。## 2, 封装:1、**封装的含义**:封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。2、封装的作用:封装的作用在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的元素并且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。### 3,如何封装:

1,将属性私有化;

2,提供有参 无参数构造;

3,提供get and set 方法;

4 提供to String 方法;

### 4、什么情况下封装:编写实例类时,用到封装有很多好处,其中比较实际的是:

拒绝直接调用声明字段,保护内部数据,更安全;
在编程中可达到缓存的效果,执行效率高;
重复调用,避免代码冗余,程序编写效率高。

### 封装

封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏

。### 继承

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式有三类:实现继承、接口继承和可视继承。

多态多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:**允许将子类类型的指针赋值给父类类型的指针。**实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不
同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function

func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间

就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是

“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函

数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只
是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

### 2.继承与多态的区别  **1、  什么是继承,继承的特点**?

子类继承父类的特征和行为,使得子类具有父类的各种属性和方法。或子类从父类继承方法,使得子类具有父类相同的行为。
特点:在继承关系中,父类更通用、子类更具体。父类具有更一般的特征和行为,而子类除了具有父类的特征和行为,还具有一些自己特殊的特征和行为。

在继承关系中。父类和子类需要满足is-a的关系。子类是父类。

表示父类和子类的术语:父类和子类、超类和子类、基类和派生类,他们表示的是同一个意思。

#### 2、  为什么需要继承?什么时候应该继承?

使用继承可以有效实现代码复用,避免重复代码的出现。

当两个类具有相同的特征(属性)和行为(方法)时,可以将相同的部分抽取出来放到一个类中作为父类,其它两个类继承这个父类。

继承实现了面向对象的原则:write once,only once(编写一次、且编写一次

#### 3、  如何实现继承?

在Java语言中,用extends(扩展)关键字来表示一个类继承了另一个类。

在父类中只定义一些通用的属性和方法。

子类自动继承父类的属性和方法,子类中可以定义特定的属性和方法。或子类重新定义父类的属性、重写父类的方法可以获得与父类不同的功能。

#### 4、  什么是方法重写?

如果在子类中定义的一个方法,其名称、返回类型及参数列表正好与父类中某个方法的名称、返回类型及参数列表相匹配,那么可以说,子类的方法重写了父类的方法。

方法重写在不同类,是实现多态的必要条件。

### 5、  super关键字的用法和位置,super关键字调用父类的构造方法,super关键字调用父类的方法?

在子类的构造方法中,通过super关键字调用父类的构造方法。

如果子类中重写了父类的方法,可以通过super关键字调用父类的方法。


位置注意:调用父类的构造方法的语句(super语句)必须是构造方法中的第一条语句。

因为创建对象的时候,需要先创建父类对象,再创建子类对象。

注意:创建对象时,先创建父类对象,在创建子类对象。如果没有显示调用父类的构造方法,将自动调用父类的无参构造方法。

#### 6、  一切类的老大(祖先)Object。

所有类都直接或者间接地继承了java.lang.Object类,Object类中定义了所有的java对象都具有的相同行为,是所有类的祖先。

一个类如果没有使用extends关键字,那么这个类直接继承自Object类。

#### **7、  什么是多态?**

多态的特征是表现出多种形态,具有多种实现方式。或者多态是具有表现多种形态的能力的特征。或者同一个实现接口,使用不同的实例而执行不同的操作。

#### 8、  为什么需要使用多态?多态的好处?

可以增强程序的可扩展性及可维护性,使代码更加简洁。

不但能减少编码的工作量,也能大大提高程序的可维护性及可扩展性。

#### 9、  如何实现多态?

一般做法是:写一个方法,它只接收父类作为参数,编写的代码只与父类打交道。调用这个方法时,实例化不同的子类对象(new 一个对象)。

更具体的说:

(1)、子类重写父类的方法。使子类具有不同的方法实现。

(2)、把父类类型作为参数类型,该父类及其子类对象作为参数转入。

(3)、运行时,根据实际创建的对象类型动态决定使用那个方法。

在运行时,java虚拟机会根据实际创建的对象类型决定使用那个方法。一般将这称为动态绑定。   **10、多态小结:多态与继承、方法重写密切相关,我们在方法中接收父类类型作为参数,在方法实现中调用父类类型的各种方法。当把子类作为参数传递给这个方法时,java虚拟机会根据实际创建的对象类型,调用子类中相应的方法(存在方法重写时)。**## 3  接口:  官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。    我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解) **接口的特点**​    

就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。

 接口指明了一个类必须要做什么和不能做什么,相当于类的蓝图。

一个接口就是描述一种能力,比如“运动员”也可以作为一个接口,并且任何实现“运动员”接口的类都必须有能力实现奔跑这个动作(或者implement move()方法),所以接口的作用就是告诉类,你要实现我这种接口代表的功能,你就必须实现某些方法,我才能承认你确实拥有该接口代表的某种能力。
如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(必须记住:抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类)
一个JAVA库中接口的例子是:Comparator 接口,这个接口代表了“能够进行比较”这种能力,任何类只要实现了这个Comparator接口的话,这个类也具备了“比较”这种能力,那么就可以用来进行排序操作了。

**为什么要用接口**

接口被用来描述一种抽象。
因为Java不像C++一样支持多继承,所以Java可以通过实现接口来弥补这个局限。

**接口也被用来实现解耦。接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?**

原因是抽象类内部可能包含非final的变量,但是在接口中存在的变量一定是final,public,static的。

接口的语法实现
为了声明一个接口,我们使用interface这个关键字,在接口中的所有方法都必须只声明方法标识,而不要去声明具体的方法体,因为具体的方法体的实现是由继承该接口的类来去实现的,因此,接口并不用管具体的实现。接口中的属性默认为Public Static Final.一个类实现这个接口必须实现这个接口中定义的所有的抽象方法。
    一个简单的接口就像这样:拥有全局变量和抽象方法。    为了实现这个接口,我们使用implements关键词去实现接口:

其中testClass类实现了我们上面刚才定义的 in1 这个接口,既然你要实现接口,也就是实现接口代表的一种能力,那么你就必须去实现接口给你规定的方法,只有把接口给你规定的抽象方法都给实现了,才承认你这个类实现了这个接口,实现了这个接口代表的某种功能。上图实现了接口中规定的display()方法。

    写一个测试类,用来测试一下我们刚才实现的这个接口,因为testclass类的对象t实现了接口规定的display方法,那么自然而然就可以调用display()方法咯。 关于接口的几个重点

我们不能直接去实例化一个接口,因为接口中的方法都是抽象的,是没有方法体的,这样怎么可能产生具体的实例呢?但是,我们可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。因此,上图中最后的方法调用我们还可以这样写:

2.一个类可以实现不止一个接口。

3.一个接口可以继承于另一个接口,或者另一些接口,接口也可以继承,并且可以多继承。

4.一个类如果要实现某个接口的话,那么它必须要实现这个接口中的所有方法。

5.接口中所有的方法都是抽象的和public的,所有的属性都是public,static,final的。

6.接口用来弥补类无法实现多继承的局限。

7.接口也可以用来实现解耦。

### 接口的标识用法
虽然接口内部定义了一些抽象方法,但是并不是所有的接口内部都必须要有方法,比如Seriallizable接口,Seriallizable接口的作用是使对象能够“序列化”,但是Seriallizable接口中却没有任何内容,也就是说,如果有一个类需要实现“序列化”的功能,则这个类必须去实现Seriallizable接口,但是却并不用实现方法(因为接口中没有方法),此时,这个Serilizable接口就仅仅是一个“标识”接口,是用来标志一个类的,标志这个类具有这个“序列化”功能。
## 10 .接口和抽象类的区别 ### 1.默认的实现方法:

(1)抽象类可以有默认的方法实现完全是抽象的。

抽象类中可以有已经实现了的方法,也可以有被abstract修饰的方法(抽象方法),因为存在抽象方法,所以该类必须是抽象类。

(2)接口根本不存在方法的实现。

但是接口要求只能包含抽象方法,抽象方法是指没有实现的方法。接口就根本不能存在方法的实现

。 ### 2.子类使用的关键词不一样: ### 3.是否有构造器:(1)抽象类可以有构造器抽象类是属于类,享有类的所有特性(但是不能实例化),当然包括类的构造方法,也就是构造器。(2)接口不能有构造器接口是所有抽象方法的集合,注意,是集合,不是类。当然没有构造方法一说,更别提什么构造器了。 ### **4.可使用的修饰符:**(1)抽象方法可以有public、protected和default这些修饰符 抽象类的目的就是被继承,抽象方法就是为了被重写,所以肯定不能用private修饰符,肯定是可以用public的。但是protected和default也是可以的。 (2)接口方法默认修饰符是public。你不可以使用其它修饰符。接口就有且只有一个public修饰。(感觉抽象类像小儿子各种耍无赖,接口就像私生子,说什么只能是什么) ### 5.速度方面:

(1)抽象方法比接口速度要快(抽象方法是小儿子,从小吃的好所以跑的快)

(2)接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。(接口是私生子,从小日子苦,营养不良)

 6.增加新方法对子类的影响:

(1)如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。

抽象类可以有一些非抽象方法的存在,这些方法被称为默认实现。如果添加一个默认实现方法(不能是抽象方法),就不需要在子类中去实现,所以继承这个抽象类的子类无须改动。

如果你往接口中添加方法,那么你必须改变实现该接口的类。

(2)接口中只能添加抽象方法,当你添加了抽象方法,实现该接口的类就必须实现这个新添加的方法。

因为,定义中说的很清楚,接口的实现必须实现所有的方法。所有,当然包括新添加的方法。

 7.子类能继承的数量:

抽象类在java语言中所表示的是一种继承关系,一个子类只能存在一个父类,但是可以存在多个接口。

java在类的继承上并没有多继承。抽象类属于类,所以可以被继承。但子类只能继承一个父类。

java为了实现多继承,使用了接口。一个类可以实现多个接口。继承就好比生了孩子,只能有一个爹,但是这个孩子可以学语文,学数学,学英语等等很多东西,而语文、数学、英语就相当于接口。

 **总的来说,因为java中抽象类只有单继承,接口就可以实现多继承。**## **11. 构造器 :**方法的定义            方法:用来描述该类实物共有的一些行为。 可以分为成员方法和静态方法。            成员方法:没有用static修饰的方法(实例方法,对象方法)。                静态方法:用static修饰的方法(类方法),可以直接通过类名直接调用。                注意:如果一个方法的返回值类型是void表示该方法没有返回值 构造方法(构造器)定义       构造方法和构造器只是翻译上的差异,实际上是同一个事物。       构造方法是一种特殊的成员方法。可以分为有参构造方法和无参构造方法。            无参构造方法:根据成员变量类型将成员变量初始化为null或0或false。            有参构造方法:将成员变量进行初始化并赋值(这里指的是通过形参传进来的实参)。  构造方法的特点:       1.构造方法方法名和类名一致,没有返回值,但不能声明void,一旦声明void,系统默认将不是构造方法。       2.可以设置关键字来控制其访问权限,但是一般情况下使用public方法权限。在默认情况下,如果不写任何关键字,其访问             权限也是public。.          3.系统会默认生成一个无参的构造方法。如果我们自己定义了一个构造方法(无参或者有参),原来默认的无参构造方法就            不存在了(系统默认新定义的构造方法覆盖了默认的无参构造)。          4.创建子类对象会调用父类构造方法但不会创建父类对象,只是调用父类构造方法初始化父类成员属性。 构造方法的作用:           在实例化对象时给对象属性赋值,即初始化成员属性而不是初始化对象。解释:实例化类之后(类实例化通过new关键字实现)才能调用类内部的属性,而在实例化类之前,系统需要给类属性分配内          存 进行存储。即在new将对象的引用返回之前初始化对象属性。​                         在这里就引申到另一个问题,构造方法有什么其他作用?             当几个变量要实现同样的功能,但是仅仅参数类型不同时,我们仅仅只需要调用不同参数的构造方法就行,不需要新定义             一个其他的方法。即:在构造方法对类进行构造的时候,只需要将功能类似的,但形参不同的方法直接打包在该类下,当             我们根据需要调用的时候,直接重载构造器中的需要的方法。             方法的重载具体参照https://blog.csdn.net/HY845638534/article/details/84112833 构造方法的使用:        1.在关键字new后面,类名+(),小括号内加上实参;                   类名 变量=new 类名(参数1,参数2);            注意:此处的参数为实参,即需要传进去的参数。如果调用无参构造方法,则不需要填写参数。如果调用了参数则必须和                     需要调用的构造方法参数(形参)参数列表相同,即类型相同,个数形同,类型顺序相同。           2.跟在关键字super或this后加小括号() ,小括号内加上实参;

public SalesEmployee(int id,double sales){
super(id,"");//这里使用了Spuer(),""也为实参,必须写。
}

这里使用了Spuer(),继承了父类中的

public Employee(int id ,String name){
this.id=id;
this.name=name;
}

this的使用则是

public Employee(int id ,String name ,double money){
this(id,name);
this.id=id;
this.name=name;
this.money=money;

}这里this()括号里面的参数是构造方法中的形参,必须名字和顺序相同,则会直接先行去实现this调用的构造方法。用在本类中。Java 8 新特性简介:1. ```1. 代码更少(增加了新语法:Lambda 表达式)   2. 强大的 Stream API(集合数据的操作)   3. 最大化的减少空指针 异常:Optional 类 的使用   4. 接口的新特性   5. 注解的新特性   6. 集合的底层 源码实现   7. 新日期时间的 api

题一:抽象类 和 接口的 异同?

抽象类:含有 abstract 修饰符的 class 就算 抽象类;它既可以有抽象方法,也可以有 普通方法,构造方法,静态方法,但是不能有抽象构造方法 和 抽象静态方法。且如果其子类没有实现其所有的 抽象方法,那么该 子类 也必须是 抽象类;接口:他可以看成是 抽象类的 一个特例,使用 interface 修饰符;内部结构: jdk7:接口只有常量和抽象方法,无构造器 jdk8:接口增加了 默认方法 和 静态方法,无构造器 jdk9:接口允许 以 private 修饰的方法,无构造器共同点: 不能实例化; 多态方式的一种使用;不同点: 抽象类是单继承的,而接口可以多继承(实现);

题二:xxx 与 yyy 的 异同?

1、overload(重载) 与 overwrite(重写) 重载:表示一个类中 可以有多个名称相同的方法,但彼此的参数不同(参数个数或参数类型),与方法的作用域和返回类型无关;  重写:表示子类中的方法可以与父类的某个方法的 名称和参数完全相同;当通过子类创建的对象调用这个方法时,将调用 子类中的定义方法,相当于将父类的此方法覆盖,这也是多态的一种表现;

2、throw 与 throws

throw:手动抛出异常,一般出现在函数体中;  throws:声明方法可能抛出的异常,一般出现在 方法头部;
3、final 、finally 与 finalize
final:用于声明属性,方法和类,表示属性不可变,方法不可重写,类不可继承;  finally:它是异常处理语句结构的一部分,表示总是会执行;  finalize:它是 Object 类的 一个方法,在 垃圾收集器 执行 的时候会调用被回收对象的 此方法,可以重写 此方法 提供垃圾收集时代的其他资源回收,例如关闭文件等。jvm 不保证此方法总被调用;

4、Collection 与 Collections

Collection:它是接口, 集合类的上级接口,继承与他有关的接口主要有List和Set; Collections:它 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作;如 Collections.sort(xxx);

5、ArrayList 与 LinkedList、Vector

ArrayList:对数组进行封装,实现长度可变的数组,和数组采用相同的存储方式,在内存中分配连续的空间。优点在于遍历元素和随机访问元素效率高;多线程不安全;     LinkedList:采用双向链表的存储方式,优点在于插入和删除元素效率高;多线程不安全;          Vector:类似于 ArrayList;但其使用了 synchronized 方法(多线程安全),使得性能上比 ArrayList 差;同时,在数组扩容时, ArrayList 是增加原来的 0.5倍,变成 1.5倍长度,而Vector 是增加 1倍,变成 2倍长度。          jdk7:创建 ArrayList 对象时,默认长度为 10,类似饿汉模式          jdk8:创建 ArrayList 对象时,默认长度为 0,在你第一次插入数据时,创建一个 长度为10 的数组,类似懒汉模式

6、String 、StringBuffer、StringBuilder

在这方面运行速度快慢为:StringBuilder > StringBuffer > String,原因如下:            String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。              在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的。StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的。          即:String:适用于少量的字符串操作的情况               	StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况     	StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

7、HashMap、LinkedHashMap、Hashtable、ConcurrentHashMap

HashMap是基于哈希表的Map接口的非同步实现(多线程不安全), 允许使用null值和null键(HashMap最多只允许一条记录的键为null,允许多条记录的值为null);      Hashtable也是一个散列表,它存储的内容是键值对。基于Dictionary类 存储数据: 首先判断value是否为空,为空则抛出异常;计     算key的hash值,并根据hash值获得key在table数组中的位置index,如果table[index]元素不为空,则进行迭代,如果遇到相同     的key,则直接替换,并返回旧value;否则,我们可以将其插入到table[index]位置。 key和value都不允许为null,Hashtable遇到null,直接返回NullPointerException。 线程安全,几乎所有的public的方法都是synchronized的,较HashMap速度慢。          ConcurrentHashMap是弱一致性,也就是说遍历过程中其他线程可能对链表结构做了调整,因此get和containsKey返回的可能是过时的数据 ConcurrentHashMap是基于分段锁设计来实现线程安全性,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。     LinkedHashMap是HashMap的一个子类,它保留插入顺序,帮助我们实现了有序的HashMap。 其维护一个双向链表,并不是说其除了维护存入的数据,另外维护了一个双向链表对象,而是说其根据重写HashMap的实体类Entry,来实现能够将HashMap的数据组成一个双向列表,其存储的结构还是数组+链表的形式。          TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。 该映射根据其键的自然顺序(字母排序)进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 TreeMap是非线程安全的。     jdk7:创建 HashMap对象时,默认长度为 16,类似饿汉模式(内部数据结构是 数组加链表);新加元素时 元素往头部添加     jdk8:创建 HashMap对象时,默认长度为 0,在你第一次插入数据时,创建一个 长度为16 的数组,类似懒汉模式(内部数据结构是 数组加链表再加红黑树);新加元素时 元素往树尾部添加

8、http 、 https

  HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。               HTTPS和HTTP的区别主要如下:          1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。     2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。     3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。     4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

9、json、gson

JSON是一种与开发语言无关的,轻量级的数据格式,全称是JavaScript Object Notation,现在几乎每种语言都有处理JSON的API。     Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库,可以将一个JSON字符串转成一个Java对象,或者反过来 。

10、sleep()/wait()

sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行 。如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。     wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行。只有其他线程调用了notify方法,调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。     【注意】notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁 。【总结】notify是告诉wait()的线程什么时候可以去继续去申请锁了。 举例:这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。     最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

11、== / equals()

 java中的数据类型分为两种:

一 、基本数据类型:

byte、short、int、long、float、double、char、boolean       比较它们需要用  ==  ,比较的是它们的值是否相等

二、引用数据类型:

    也就是对基本数据类型的封装,用 == 比较的是它们的内存地址(其实还是比较的基本数据类型,它们的内存地址不就是int吗)。       当new的时候,会给它一个新的内存地址,所以再通过==比较,就会返回false;在Object类中的equals方法其实比较的也是内存地址,用==和equals方法比较结果是一样的,但在一些类中把equals方法重写了,如String、Integer等类中,而不是单纯的比较内存地址了,而是比较内容或者值是否相等。                这个equals方法不是固定的,有需要的时候,我们根据情况自己重写。

12、cookie / session

  cookie数据存放在客户的浏览器上,session数据放在服务器上。          cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗    考虑到安全应当使用session。         session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能    考虑到减轻服务器性能方面,应当使用COOKIE。         单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。 所以个人建议:         将登陆信息等重要信息存放为SESSION        其他信息如果需要保留,可以放在COOKIE中

13、DOM / SAX 解析

  SAX解析方式:逐行扫描文档,一遍扫描一遍解析。相比于DOM,SAX可以在解析文档的任意时刻停止解析解析,是一种速度更快,更高效的方法。 优点:解析可以立即开始,速度快,没有内存压力 缺点:不能对结点做修改 ;适用于读取 XML文件。               DOM解析方式:DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点) 优点:把XML文件在内存中构建属性结构,可以遍历和修改节点。 缺点:如果文件比较大,内存有压力,解析的时间会比较长。适用于修改 XML文件

14、stack / queue

栈与队列的相同点:          1.都是线性结构。          2.插入操作都是限定在表尾进行。         3.都可以通过顺序结构和链式结构实现。         4.插入与删除的时间复杂度都是O(1),在空间复杂度上两者也一样。         5.多链栈和多链队列的管理模式可以相同。                                          栈与队列的不同点:               1.删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。          2.应用场景不同;常见栈的应用场景包括括号问题的求解,表达式的转换和求值,函数调用和递归实现,深度优先搜索遍历等;常见的队列的应用场景包括计算机系统中各种资源的管理,消息缓冲器的管理和广度优先搜索遍历等。          3.顺序栈能够实现多栈空间共享,而顺序队列不能。

15、集合类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S1AtcPBm-1629458232565)(D:\typro _java 笔记\java 基础\Java基础图片\70)]

Collection是一个接口,它主要的两个分支是:List 和 Set。     List和Set都是接口,它们继承于Collection。List是有序的队列,List中可以有重复的元素;而Set是数学概念中的集合,Set中没有重复元素!          List和Set都有它们各自的实现类。           为了方便,我们抽象出了AbstractCollection抽象类,它实现了Collection中的绝大部分函数;这样,在Collection的实现类中,我们就可以通过继承AbstractCollection省去重复编码。AbstractList和AbstractSet都继承于AbstractCollection,具体的List实现类继承于AbstractList,而Set的实现类则继承于AbstractSet。             另外,Collection中有一个iterator()函数,它的作用是返回一个Iterator接口。通常,我们通过Iterator迭代器来遍历集合。Lis
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值