BAT大厂Android面试题Java部分问题和答案整理(一)

Java基础知识整理

  • [x] java中==和equals和hashCode的区别
  • [x] int、char、long各占多少字节数
  • [x] int与integer的区别
  • [x] 谈谈对java多态的理解
  • [x] String、StringBuffer、StringBuilder区别?
  • [x] 什么是内部类?内部类的做用?成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用?
  • [x] 静态内部类的设计意图?
  • [x] 抽象类和接口区别
  • [x] 抽象类的意义
  • [x] 抽象类与接口的应用场景
  • [x] 抽象类是否能够没有方法和属性?
  • [x] 接口的意义
  • [x] 泛型中extends和super的区别
  • [x] 父类的静态方法可否被子类重写
  • [x] 进程和线程的区别
  • [x] final,finally,finalize的区别
  • [x] 序列化的方式
  • [x] Serializable 和Parcelable 的区别
  • [x] 静态属性和静态方法是否能够被继承?是否能够被重写?以及缘由?
  • [x] 谈谈对kotlin的理解
  • [x] 闭包和局部内部类的区别
  • [x] string 转换成 integer的方式及原理

Java深刻点的知识点

  • [ ]哪些状况下的对象会被垃圾回收机制处理掉?
  • [ ]讲一下常见编码方式?
  • [ ]utf-8编码中的中文占几个字节;int型几个字节?
  • [ ]静态代理和动态代理的区别,什么场景使用?
  • [ ]Java的异常体系
  • [ ]谈谈你对解析与分派的认识。
  • [ ]修改对象A的equals方法的签名,那么使用HashMap存放这个对象实例的时候,会调用哪一个equals方法?
  • [ ]Java中实现多态的机制是什么?
  • [ ]如何将一个Java对象序列化到文件里?
  • [ ]说说你对Java反射的理解
  • [ ]说说你对Java注解的理解
  • [ ]说说你对依赖注入的理解
  • [ ]说一下泛型原理,并举例说明
  • [ ]Java中String的了解
  • [ ]String为何要设计成不可变的?
  • [ ]Object类的equal和hashCode方法重写,为何?

问题解答部分

1. java中==和equals和hashCode的区别?

  • == 是判断相等运算符,用于比较基本数据类型的值是否相等,或者引用类型对象的内存地址是否相等。
  • equals 是Object类的一个方法,子类能够重写这个方法,实现特定类的equals方法。经常使用于判断两个对象的内容是否一致(实际上若是你关注的仅仅是人的年龄,你可让一个男人equals一个女人。是否equal的规则是由本身定义的,可是通常状况下重写equals方法仍是有一些要求的。)。Object类的equals方法的默认实现只是比较了对象的内存地址是否同样(obj1==obj2),因此咱们在须要的时候须要本身重写equals方法来判断对象内容是否一致。equals方法的重写原则是:
  1. 自反性 x.equals(x) = true
  2. 对称性 x.eqlas(y) = y.equals(x)
  3. 传递性 x.eqlas(y) = true && y.equals(z)=true 则 x.equals(z)=true
  4. 一致性 每次调用返回的结果应该一致(若是参与equals判断的字段没有改变)
  5. x.equals(null) = false
  6. x==y 则x.equals(y)=true
  7. equal objects must have equal hash codes
  8. 若是x.equals(y),若是x和y的类型不一致,应该返回false
  • hashCode 是对象的一个标示值,是根据对象内的值进行运算获得的结果。把一个须要占据更多内存的对象用一个占用更少内存的HashCode值来表示,所以确定会有信息损失,致使的结果是不一样的对象的HashCode值可能会相等。经常用在HashMap或者HashTable中,hashCode方法的规则以下:

>
1. 一个应用程序一个执行片断(线程)中HashCode值应该不变,可是并不须要保证在两个不一样的执行片断中HashCode值也要相等
2. equal的两个对象的HashCode值须要保证相等
3. not equal的两个对象的HashCode值不须要保证不相等,可是若是能够不一致可以提升HashMap等的效率(防止碰撞的发生)
4. Object类的hashCode方法是根据对象在内存中的地址转换出的 java

个人另一篇博客有专门写到这个git

2. int、char、long各占多少字节数?

Java中有八种基本数据类型: web

  1. byte 1个字节
  2. char 2个字节
  3. boolean java没有说明必定是几个字节,可认为是一个字节,毕竟只要一个bit就能够表示true和false两种状况了
  4. short 短整型 2个字节
  5. int 整型 4个字节
  6. long 长整型 8个字节
  7. float 浮点数类型 4个字节
  8. double 双精度浮点数类型 8个字节

3. int与integer的区别?


  • Java中的数据类型分为基本数据类型和引用数据类型,因此有人说Java不是一门纯粹的面向对象语言。int是基本数据类型,Integer是int的包装类。讲到这个不得不说一说自动装箱和自动拆箱。自动装箱和自动拆箱是Java5新增的概念,Java5之前须要使用手动装箱和拆箱。
  • 自动装箱指的是基本数据类型自动转换成想对应的包装类型,如int转换成Integer会自动调用Integer.valueOf(int a)方法将int值转换成对应的Integer包装类。(这里有一点值得注意的是:-128~127之间的int值装箱时指向的是缓存对象,不在这里范围内则会新new一个Integer对象面试

public static Integer valueOf(int i) {
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
   }
  • 自动拆箱指的是包装类型自动转换成对应的基本数据类型的过程。如Integer自动转换成int、Character自动转换成char,分别调用integer.intValue()、character.charValue()。算法

  • 自动装箱和自动拆箱的发生时机:当须要int值,可是传入的参数是Integer类型时会发生自动拆箱,相反则会发生自动装箱
  • 其余须要额外注意的点:
    1. integer += 2,会进行一次自动拆箱和一次自动装箱,使用时须要注意。
    2. 发生重载时不会发生自动装箱和拆箱操做
    3. int == int ,int == Integer 比较的是值。Integer == Integer比较的是对象地址.
    4. 前面说的那个-128~127的问题。贴一段源码本身看哦,小伙伴们

    5. /** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #Integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code Integer} instance representing {@code i}. * @since 1.5 */
           public static Integer valueOf(int i) {
              if (i >= IntegerCache.low && i <= IntegerCache.high)
                  return IntegerCache.cache[i + (-IntegerCache.low)];
              return new Integer(i);
          }
  • /** * Cache to support the object identity semantics of autoboxing for values between * -128 and 127 (inclusive) as required by JLS. * * The cache is initialized on first usage. The size of the cache * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option. * During VM initialization, java.lang.Integer.IntegerCache.high property * may be set and saved in the private system properties in the * sun.misc.VM class. */
        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 =
                    sun.misc.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() {}
        }

    4.谈谈对java多态的理解。

    • 是什么。多态是面向对象的三大特性之一,面向对象的三大特性是:继承、封装和多态。多态指的是:父类的引用变量 指向子类对象,当调用父类中存在的方法时,实际上会调用子类重写以后的方法(若是有重写。不然调用父类的方法。)
    • 为何。在开发中会常常遇到设计必定的准则A,你们都来遵循这个准则A来办事。准则A一、A2…的具体实行留给实施该准则的地方来实现,可是怎么调用到某一个特定的准则A1呢,有两个方法,一个是强制类型转换以后将A转换成A1,而后调用A1重写的规则,再有就是创造一个方法可使用A直接调用A1表明的准则,就是多态了。
    • 怎么作。多态的实现须要三个条件:
    • 继承+重写:子类A1继承自父类A,而且重写父类A的方法function1();
    • 向上转型:使用父类A的引用变量指向A1对象,如A a = new A1();
    • 使用a.function1();

    当使用a调用function1的时候,实际上调用的是A1重写的function1方法。编程

    • 来个实例:例子是网上流传比较广的例子,找不到出处了,谁知道出处告诉我一下,我把原文连接贴出来。
    public class Main {
        public static void main(String[] args) {
            A a1 = new A();
            A a2 = new B();
            B b = new B();
            C c = new C();
            D d = new D();
            System.out.println("1--" + a1.show(b));
            System.out.println("2--" + a1.show(c));
            System.out.println("3--" + a1.show(d));
            System.out.println("4--" + a2.show(b));
            System.out.println("5--" + a2.show(c));
            System.out.println("6--" + a2.show(d));
            System.out.println("7--" + b.show(b));
            System.out.println("8--" + b.show(c));
            System.out.println("9--" + b.show(d));
        }
    
        public static class A {
            public String show(D obj) {
                return ("A and D");
            }
            public String show(A obj) {
                return ("A and A");
            }
        }
    
        public static class B extends A {
            public String show(B obj) {
                return ("B and B");
            }
            public String show(A obj) {
                return ("B and A");
            }
        }
    
        public static class C extends B {}
        public static class D extends B {}
    
        /** * * 下面这是正确答案.你对了几个? * 1--A and A * 2--A and A * 3--A and D * 4--B and A * 5--B and A * 6--A and D * 7--B and B * 8--B and B * 9--A and D * */   
     }

    下来作下简单说明:
    根据继承关系咱们知道: C/D->B->A
    根据继承关系关系,咱们把类A和类B的方法作一下简化。 数组

    class A{
        代号F1:show(A) 输出 A and A
        代号F2:show(D) 输出 A and D
    }
    
    class B extends A{
        代号F3:show(A) 输出 B and A   **重写的方法,覆盖父类的show(A)方法
        代号F4:show(B) 输出 B and B   **新增长的方法
        代号F5:show(D) 输出 A and D   **继承自A类的方法,没有重写。
    }
    
    再看一下引用对象:
    A a1->A 引用变量和实际对象一致,能够调用类A中的方法(F1,F2)
    A a2->B 引用变量和实际对象不一致,作了向上转型,此时a2只能调用继承自A类的方法或者重写了A类的方法,不能调用B类本身新增的方法。(F3,F5)
    B b ->B 引用变量和实际对象一致,能够调用类B中的全部方法,包括继承来的、重写的、新增的。(F3,F4,F5)
    
    在进行方法的选择时,首先看能调用到哪些方法,而后在能够调用的方法里寻找是否能完美匹配实参真实类型的,若是没有,向上转型找到最近的一个父类。
    
    1-3都是a1调用方法,a1的变量类型和实际类型一致,能够调用F1,F2
    System.out.println("1--" + a1.show(b)); //b是B的实例,可是A类没有show(B)方法,因此须要对b向上转型,而后b是A的子类的实例,因此调用F1,输出-> A and A
    System.out.println("2--" + a1.show(c));//c跟B同样须要作向上转型,调用F1,输出-> A and A
    System.out.println("3--" + a1.show(d));//d是D的实例,能够直接调用F2,输出A and D
    4-6 都是a2调用方法,能够调用F3,F5
    System.out.println("4--" + a2.show(b));//b是A的子类的实例,调用F3,输出B and A
    System.out.println("5--" + a2.show(c));//c是A的子类的实例,调用F3,输出B and A 
    System.out.println("6--" + a2.show(d));//d是D的实例,调用F5,输出A and D
    7-9 都是a2调用方法,能够调用F3,F4,F5
    System.out.println("7--" + b.show(b));//b是A的子类的实例,实际调用时调用F4,输出B and B
    System.out.println("8--" + b.show(c));//c是B的子类的实例,调用F4,输出B and B
    System.out.println("9--" + b.show(d));//d是D的实例,调用F5,输出A and D
    
    这样一加分析,是否是就很清楚明了了呢。

    5.String、StringBuffer、StringBuilder区别?

    **相同点:**String、StringBuilder、StringBuffer都是使用一个char[]保存字符串数据。
    **不一样点:**String 是不可变的,StringBuilder是可变的线程不安全的,StringBuffer是在StringBuilder的基础上加上了线程安全控制,因此是可变的线程安全的。
    面试官可能会深刻到下面的问题缓存

    • 字符串池:String是不可变的,共享使用能够提升效率。不可变,因此全部改变字符串的方法都是new了一个新String(可能会借助SB)。contact用的是把原来的数据和新增的数据copy到另外一个char[]中,
    • 内存模型:常量池放在栈内存区,new对象放在堆内存区。String s0 = "abc";String s1 = "abc";String s2 = new String("abc");s0和s1指向的是同一个对象(常量池中的对象);s1和s2指向的不是同一个对象。
    • 线程安全:这是另外一个话题
    • new String,String+String,String.replace,String.concat等方法的内部实现
    • 或者其余的

    6.什么是内部类,内部类的做用?成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用?

    定义:把一个类定义在另一个类中,这个类就是内部类。
    做用(意义):内部类能够实现更好的封装;内部类的使用能够实现多重继承;若是一个类须要实现的两个接口中有同名同参数列表的方法(或者父类和接口),也须要借助内部类来实现;内部是面向对象中的闭包。
    内部类能够分为下面几种:
    成员内部类包含_非静态内部类_和_静态内部类_
    局部内部类包含_通常局部内部类_和_匿名内部类_ 安全

    下面的不少内容都涉及了成员间的相互访问能力,这里有一条准则是必定要牢记的: 网络

    静态成员不能访问非静态成员!

    • 非静态内部类:
    • 定义:类定义在外部类的大括号里,做为外部类的成员。非静态内部类不能拥有静态成员(静态方法、静态成员、静态初始化块)。
    • 访问外部类:内部类能够直接访问外部类的全部静态成员和非静态成员(内部类做为外部类的成员,成员间的相互访问是不受访问控制符的限制的)。
    • 被外部类访问:外部类须要新建一个内部类的实例才能访问内部类的非静态成员(根据第二条规则,事实上也不会有静态成员),不受访问控制符的限制。
    • 被其余类访问:在外部类之外访问内部类首先须要肯定是否有内部类的访问权限,若是能够访问内部类,则须要新建一个外部类的实例,而后用外部类的实例建立内部类对象进行访问。
    • 做用与应用:由于非静态内部类不能脱离离开外部类实例单独存在,因此特别适用于内部类是外部类不可分割的一部分,内部类实例离开外部类实例就没有意义的那种状况(如响尾蛇是一个实例,响尾蛇的尾巴是一个实例,响尾蛇的尾巴离开响尾蛇就不会响了,这个尾巴的成员变量和成员方法就失去了意义)。或者添加上访问控制符,不想让内部类脱离外部类使用的状况,如一个RecyclerView的Adapter是一个外部类,Adapter中的ViewHolder是一个内部类,这个ViewHolder脱离里这个Adapter就没有了意义,因此能够给这个ViewHolder添加private访问控制符不让外部类之外的类访问。
    • 静态内部类:
    • 定义:类定义在外部类的大括号里,做为外部类的成员,而且增长Static关键字修饰。
    • 访问外部类:静态内部类能够访问外部类的静态成员变量和静态成员方法,不能访问外部类的非静态成员。
    • 被外部类访问:外部类须要新建一个内部类的实例才能访问内部类的非静态成员,或者直接使用内部类类名访问内部类的静态成员。
    • 被其余类访问:在外部类之外能够直接使用外部类名.内部类名访问内部类的静态成员,或者须要新建一个内部类的实例来访问内部类的非静态成员。
    • 做用与应用://todo 这个我不知道诶我也还不是很明白静态内部类的应用场景呢。
    • 局部内部类:
    • 定义:类定义在外部类的方法中,而且有本身的类名。局部内部类至关于一个局部变量,因此不能有访问控制符以及static等修饰符。局部内部类的访问(包括对象的建立、方法的调用)局限在外部类的方法内部。
    • 访问限制:局部内部类能够访问方法内的局部变量,可是局部变量须要是final类型的(Java8之前)
    • 做用与应用: 通常的局部内部类确实用的比较少诶
    • 匿名内部类
    • 定义:匿名内部类继承自一个类或者实现一个接口,使用方法是直接使用new 父类名称或者接口名称就能够建立本身匿名内部类的对象。
    • 内部类的构造器:匿名内部类没有类名,因此没法定义构造器。若是是继承父类的匿名内部类,新建对象是直接使用父类的构造器。
    • 做用与应用: 最多见的建立匿名内部类的方式是在回调时须要某个接口类型的对象。

    7.静态内部类的设计意图?

    若是有一个内部类并不须要保存一个外部类的引用时,能够设计成静态类;
    若是外部类有一个静态方法须要使用到内部类中的某个成员(成员变量或者成员方法),那么也须要把内部类设置成静态的。

    8.抽象类和接口区别?

    • 设计目的:接口体现的是一种规范和实现相分离的设计哲学,接口定义了一系列的准则供实现类来实现。实现此接口的全部类都遵循了接口所定义的一系列准则,他们内部实现不同,可是对于调用者而言他们能够提供相同的对外服务。面向接口的耦合是一种低层次的耦合,能够提供更好的拓展性和可维护性。
      抽象类则提现的是一种模板模式的设计。抽象类定义了一种模板,完成模板的一些基础实现,其余的细节交给子类去延迟实现。
    • 用法(包含的内容):
    1. 变量:抽象类能够包含任何类型的成员变量;接口只能包含public static final类型的变量。
    2. 方法:抽象类能够包含抽象方法或者非抽象方法(能够有方法的实现);接口只能包含抽象实例方法(java8之后能够包含类方法和默认方法)。
    3. 初始化块和构造器:抽象类能够包含,接口则不能包含。
    • 其余:抽象类的关键字abstract class,接口的关键字interface。

    9.抽象类的意义?

    抽象类是值用abstract关键字修饰的类,抽象类中能够包含抽象方法而且不能被实例化,除此以外,抽象类和其余的类并无本质性的区别(甚至能够把抽象类当作普通类和接口的复合体,或者说是把抽象类当作接口和具体实现类之间的一个过渡,好比说List接口—>AbstractList抽象类—>ArrayList具体实现类)。

    • 抽象类提取了子类中的公有方法,能够提供方法级别的代码复用;
    • 同时抽象类的实现类必须实现抽象类中定义的抽象方法,向外提供一致的调用方法。
    • 抽象类将子类的公共部分进行提取,将可变部分留给子类去实现,子类可同时拥有父类的能力,也拥有本身新的能力。
    • 抽象类是用于继承的,能够实现多态。

    10.抽象类与接口的应用场景?

    • 从意义上说:抽象类是对一组具备关联(有共同的属性、方法或者意义上有关联等)的类的抽象;接口则会是对一组执行相同标准(如都有List接口的子类都有插入、删除等功能)的类的抽象。从这个层面来讲,抽象类适用于子类之间有一些共同的属性或者方法,不须要子类去单独实现(可是子类也能够有本身独特的属性或方法)的时候;而接口适用于子类可能只是须要实现特定的一组功能,对其余的并无约束。
    • 从类之间通信:接口更加适合用于两个类之间须要经过一个标准进行通信,具体实现并不关心(面向接口编程);抽象类则会在若是只靠纯粹的接口没法知足通信需求(有状态等信息须要保存),能够考虑使用抽象类。
    • 从功能上来区分:值得注意的是:
    • 接口不能拥有实例变量,若是子类之间须要共同的变量,只能使用抽象类
    • 接口的全部方法都必需要被子类实现,若是你设计的方法只想被须要的时候实现,你应该使用抽象类(Java8之前,Java8之后接口也能够有默认方法 default method)。
    • 你只须要一组标识,不须要任何方法,应该使用接口(只是使用了接口的变量都是public static final 的这一特性)

    11.抽象类是否能够没有方法和属性?

    能够。抽象类和实现类之间的惟一区别是抽象类中容许存在抽象方法。

    12.接口的意义?

    接口,在Java中叫Interface,在Object_C中叫protocol(协议),在维基百科上接口的解释是用于沟通的中介物的抽象
    咱们在生活中常常会遇到一种状况就是咱们但愿这个东西能够作什么,可是并不想了解具体是怎么作的,好比说电饭锅能够烧饭,无论什么品牌什么型号的电饭锅均可以烧饭,咱们并不须要知道电饭锅怎么烧饭的,不一样品牌的电饭锅烧饭有什么区别,可是咱们都知道只要是电饭锅是确定是能够烧饭的(若是不能烧饭咱们就要去12315投诉了)。
    在软件开发中咱们也须要对一些共通的内容进行抽象,若是咱们常常须要比较两个对象的大小,任意的对象都应该有比较大小的方法,比较的过程由具体的对象施行。若是咱们有一个规范A(或者协议)规定了比较大小的方法a,那么咱们只须要知道某个类B是否实现了这个接口A,若是实现了,那咱们知道B确定有一个方法B.a是用来比较对象B一、B2的大小的,咱们直接调用就行了。

    13.泛型中extends和super的区别

    extends 肯定了泛型类型的上限。
    super 肯定了泛型类型的下限。

    假如如今有如今的继承关系:

    • Animal extends Object
    • Person extends Animal
    • Students extends Person
    • CollegeStudents extends Student

    如今假设有一个List:

    List<? extends Person> personList = new ArrayList<>();
    //那么List可接受类型只能是Person、Student、CollegeStudent,
    //不能是Animal,不能是Object
    
    
    List<? extends Person> personList = new ArrayList<>();
    //那么List可接受类型只能是Animal或者Object
    //不能是Person、Student、CollegeStudent

    14.父类的静态方法可否被子类重写?

    不能。这个问题多是想问你类加载以及对象建立和方法调用的过程,这是一大串内容啊,回头有时间再专门写则这个话题吧。
    简单点就能够直接回答:我试了,加上@override以后编译不经过。

    15.进程和线程的区别

    进程: Process,进程是操做系统进行资源分配(内存等)的单位。一个应用至少拥有一个进程(一个应用也能够开启两个进程),一个进程默认拥有一个线程。
    线程: Thread,线程是CPU进行调度的单位。隶属于同一个进程的不一样线程共享该进程的系统资源,共享一块内存,并发执行的效率更高。线程比进程更加轻量,多个线程之间进行交互更加方便。

    16.final,finally,finalize的区别

    final: 用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.

    finally: 是异常处理语句结构的一部分,表示老是执行.

    finalize: 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,能够覆盖此方法提供垃圾收集时的其余资源回收,例如关闭文件等. JVM不保证此方法总被调用.

    17.序列化的方式

    18.Serializable 和Parcelable 的区别

    Java自定义了两种序列化方式,实现Serializable接口或者Externalizable接口。
    Android提供了一种序列化的方式,实现Parcelable接口。
    区别是:

    • 实现Serializable接口不须要写任何额外的代码;实现Parcelable接口则须要实现接口里的方法,以及建立一个Creator,来完成格式化。
    • 实现Serializable接口序列化的对象适合硬盘存储、网络存储等;实现Parcelable接口适合于在内存中传输。
    • 实现Serializable接口实现序列化的过程运用了反射技术,因此效率比较低;实现Parcelable接口则序列化的速度比较快。

    19.静态属性和静态方法是否能够被继承?是否能够被重写?以及缘由?

    能够被继承,不能被重写。
    Java中的绑定指的是将方法的调用与具体的类和类中的方法作一个链接,绑定分为静态绑定和动态绑定。static静态方法的绑定属于静态绑定,在程序执行前已经由Java编译器或者其余链接器进行了绑定,并非在运行时进行的绑定。

    重写指的是Java子类重写(@Override)和覆盖父类的方法,是多态的基础,多态是发生在运行时的。子类能够建立和父类同样的静态方法,可是这时候不是重写,只是子类也创建了一个和父类同样的静态方法而已,引用变量是什么类型,就调用哪一个类型的静态方法。

    更严格意义上说,我以为Java对静态成员的调用就不应使用实例.静态成员,而应该只容许使用类名.静态成员,这样才更符合静态成员属于类自己而不属于类的实例这一特性。

    20.谈谈对kotlin的理解

    个人理解是这玩意儿挺好的,可是也不是特别须要急着学,可是会这个确定是好的,毕竟是趋势。

    说说我用的时候感触比较深的几点。

    • 与Java的互通性
    • 空安全,不再用担忧空指针了
    • 字符串的链接更爽,尤为是多个固定字符串和多个变量的链接,超爽
    • 类型推断,定义而且初始化时不须要显式指明变量类型
    • 方法能够设置默认参数,挺实用的
    • public 变量不须要写getter和setter方法了,虽然之前也能够用工具生成,可是代码老是显得有些臃肿。
    • 更好的Lambda表达式

    21.闭包和局部内部类的区别

    先说说闭包,能够先参考下这里,评论也看一下
    闭包的概念:外部环境持有内部函数所使用的自由变量,由此对内部函数造成了闭包。一个函数的自由变量就是既不是局部变量也不是方法参数的变量。
    在Java中,函数只能在类中定义。因此这里的内部函数必定是在一个类中的。

    我的认为Java中的几种闭包的常见形式:

    • 类直接做为外部环境: 若是这个函数直接使用了类中的实例变量,那么函数的返回值既依赖于函数的参数,同时也依赖于类的实例变量,这也是一种闭包。
    • 类做为包装,放在另外的类里:这是外部类-非静态内部类的一种闭包形式,内部类持有外部类的引用,若是此时内部类的成员方法调用了外部类的实例变量,则外部类就对该函数实现了简洁的闭包。
    • 类做为包装,放在其余的方法里:这是一种方法-局部内部类的闭包形式,局部内部类引用了方法中的局部变量,也就造成了闭包。

    局部内部类的概念:将类定义在方法、代码块的内部就造成了局部内部类,局部内部类的做用范围只在方法或者代码块的做用范围内有效。

    根据前面列出的几种造成闭包的形式能够看出,局部内部类能够构成闭包,闭包的造成并不必定须要局部内部类。局部内部类是造成闭包的非充分非必要条件。

    22.string 转换成 integer的方式及原理。

    String转换成Integer须要使用Integer类的Integer.parseInt(String)方法。
    把String转换成Integer的方法须要考虑如下几点:
    我以为1-4都比较容易判断,第5点稍微有点麻烦,看源码看了半天才明白意思。

    1. String的是否为null,总长度大于零
    2. 数字部分的长度是否大于0
    3. 字符串的第一为是否标示正负,以及数字的正负
    4. 除正负位之外的其余位是否都是“0-9”之间的数字
    5. 数字大小是否超过Integer能表示的范围

    下面说一下源码中转换的流程。(以转换成10进制为例子,暂时不考虑其余进制)

    1. 判断条件1,是null或者长度为0,直接抛出异常。
    2. 判断条件3,而且标记是正数仍是负数,*同时把正数当作负数来处理,最后再作符号的转换*。而后判断条件2,数字部分长度小于1直接抛异常。
    3. 循环
    1. 判断条件4,同时拿到当前位表明的数字digit;
    2. 方法一判断5;(方法一的具体逻辑稍后再说)
    3. 而后将digit乘以10存为结果result。result *= 10
    4. 方法二判断5;(方法二的具体逻辑稍后再说)
    5. 将原来的result和当前位进行合并。result -= digit。由于前面说了当成负数处理的,因此这里result = result-digit。
    1. 根据以前标记的符号位的正负转换成真实的数字。return 是负数?result:-result

    下面来讲一下前面预留的几个没有解决的问题:

    1. 为何转换成负数处理不转换成正数处理,或者分开处理:
    • 负数的标示范围比正数大,因此能够把正数转换成负数来进行运算,都转成正数会出问题.
      说一个常见的数字Integer的范围是-2147483648~2147483647,在判断是否超过最大值的时候须要使用,因此咱们用同一个变量来标示边界值(最大或者最小)若是用int boundary = Integer.MAX_VALUE是没有问题的,可是若是使用int boundary = -Integer.MIN_VALUE这就出问题了,由于int类型变量不能表示2147483648。(我当时也弄不明白为何不转成正数,后来才发现原来最小的负数的绝对值比正数大1。)
      • 我的感受若是是我本身写代码,我就分开写了,虽然有不少代码是重复的,可是感受逻辑性和可读性会好不少。可是源码没有这么作。

    我来贴一段源代码吧

    public static int parseInt(String s, int radix)
                throws NumberFormatException
        {
            /* * WARNING: This method may be invoked early during VM initialization * before IntegerCache is initialized. Care must be taken to not use * the valueOf method. */
    
    
            if (s == null) {
                throw new NumberFormatException("null");
            }
    
            if (radix < Character.MIN_RADIX) {
                throw new NumberFormatException("radix " + radix +
                        " less than Character.MIN_RADIX");
            }
    
            if (radix > Character.MAX_RADIX) {
                throw new NumberFormatException("radix " + radix +
                        " greater than Character.MAX_RADIX");
            }
    
            int result = 0;//用来保存结算结果
            boolean negative = false;//是不是负数
            int i = 0, len = s.length();//String下标;数组长度
            int limit = -Integer.MAX_VALUE;//边界值,此处是最小值。把正数的最大值也用负数表示
            int multmin;// 在这里等于limit/10,limit值的除了最后一位之外的几位的值在这里应该是214748364
            int digit;//用来保存String的每一位表明的数字
    
            if (len > 0) {
                char firstChar = s.charAt(0);
                if (firstChar < '0') { // Possible leading "+" or "-"
                    if (firstChar == '-') {
                    //若是是负数
                        negative = true;
                        limit = Integer.MIN_VALUE;
                    } else if (firstChar != '+')
                        throw NumberFormatException.forInputString(s);
    
                    if (len == 1) // Cannot have lone "+" or "-"
                        throw NumberFormatException.forInputString(s);
                    i++;
                }
                multmin = limit / radix;//这个值 后面有用。
                while (i < len) {
                    // Accumulating negatively avoids surprises near MAX_VALUE
                    //Character.digit获取到字符表明的数字,这里也有一个小算法
                    digit = Character.digit(s.charAt(i++),radix);
                    if (digit < 0) {
                    //若是不是0-9之间的数字 直接抛异常
                        throw NumberFormatException.forInputString(s);
                    }
                    if (result < multmin) {
                        //这里用的是一种提早判断防止越界的办法,若是在result *= radix以后判断时候比最小值小,则可能存在数值越界的问题,为了不数值越界的问题,能够直接提早进行比较,就是比较计算以前的数值书否会越界。
                        throw NumberFormatException.forInputString(s);
                    }
                    result *= radix;
                    if (result < limit + digit) {
                        //这里也是一种提早判断的方法,若是result-digit已经比limit还小,就确定已经超过int能表示的最小的数了。因此要提早作一下比较(提早比较的时候没有赋值,result-digit的类型巴拉巴拉),避免赋值的时候出现越界处理。
                        throw NumberFormatException.forInputString(s);
                    }
                    result -= digit;//若是没有越界,就把后面的数值加到结果里。
                }
            } else {
                throw NumberFormatException.forInputString(s);
            }
            return negative ? result : -result;//根据正负号返回最终结果。
        }

    因此这道题,简单描述的答案就是:
    把字符串的第一个数字拿出来乘以十获得一个数字,再把获得的数字加上第二个数字获得一个数字;再上一轮拿获得的数字乘以十,再加上第三个数字获得一个结果,这个结果做为下一轮的初始值,以此类推。这中间可能会穿插一些其余细节的判断和处理。