Java基础回顾——重难点易错点【2】

看过才明白,我自己的基础漏洞还真是不少,不知道大牛们是不是跟我一样也有过这个阶段q.q

  1. Java中修饰符总共有一下几种:
    1.访问控制修饰符
    分别有:public private protected,缺省
    2.其它修饰符
    分别有:abstract,final,static,native,synchronized,transient,volatile等
    (1)对于形式参数只能用final修饰符,其它任何修饰符都会引起编译器错误 。但是用这个修饰符也有一定的限制,就是在方法中不能对参数做任何修改。
    (2)不过一般情况下,一个方法的形参不用final修饰。只有在特殊情况下,那就是:方法内部类。
    (3)一个方法内的内部类如果使用了这个方法的参数或者局部变量的话,这个参数或局部变量应该是final。

  2. 接口里面的变量默认都是public static final 的,它们是公共的,静态的,最终的常量.相当于全局常量,可以直接省略修饰符。
    这里写图片描述
    实现类可以直接访问接口中的变量 。

  3. 类内部静态变量
    示例:

    public class HasStatic {// 1
    private static int x = 100;// 2
    public static void main(String args[]) {// 3
        HasStatic hsl = new HasStatic();// 4
        hsl.x++;// 5
        HasStatic hs2 = new HasStatic();// 6
        hs2.x++;// 7
        hsl = new HasStatic();// 8
        hsl.x++;// 9
        HasStatic.x--;// 10
        System.out.println(" x=" + x);// 11
    }
    }

    结果为:“x=”102
    由于x是static的,存贮在类内,而不是对象内,所以++、–操作的是同一个变量。类变量既可以通过类来返回,也可以通过类的对象来访问。但通过类的对象来访问类变量时,实际不是访问该对象所拥有的变量,因为当系统创建该类的对象时,系统不会再为类变量分配内存,也不会再次对类变量进行初始化,也就是说,对象根本不拥有对应类的类变量,通过对象访问类变量只是一种假象,通过对象访问的依然是该类的类变量。

  4. 几个结构模式定义
    结构型模式是描述如何将类对象结合在一起,形成一个更大的结构,结构模式描述两种不同的东西:类与类的实例。故可以分为类结构模式和对象结构模式。
    在GoF设计模式中,结构型模式有:
    1.适配器模式 Adapter
    适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
    两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
    2.桥接模式 Bridge
    桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
    3.组合模式 Composite
    组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
    4.装饰模式 Decorator
    装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。 装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。
    5.外观模式 Facade
    外观模式为子系统中的一组接口提供了同意的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
    外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
    6.享元模式 Flyweight
    享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。.
    享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。
    7.代理模式 Proxy
    为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。

  5. jvm堆内存问题
    (单选题) 下面哪种情况会导致持久区jvm堆内存溢出?
    A 循环上万次的字符串处理
    B 在一段代码内申请上百M甚至上G的内存
    C 使用CGLib技术直接操作字节码运行,生成大量的动态类
    D 不断创建对象
    JVM堆内存分为2块:Permanent Space 和 Heap Space。
    Permanent 即 持久代(Permanent Generation),主要存放的是Java类定义信息,与垃圾收集器要收集的Java对象关系不大。
    Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年轻代(Young Generation)。年老代和年轻代的划分对垃圾收集影响比较大。
    年轻代
    所有新生成的对象首先都是放在年轻代。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代一般分3个区,1个Eden区,2个Survivor区(from 和 to)。
    大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当一个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将可能被复制到年老代。
    2个Survivor区是对称的,没有先后关系,所以同一个Survivor区中可能同时存在从Eden区复制过来对象,和从另一个Survivor区复制过来的对象;而复制到年老区的只有从另一个Survivor区过来的对象。而且,因为需要交换的原因,Survivor区至少有一个是空的。特殊的情况下,根据程序需要,Survivor区是可以配置为多个的(多于2个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
    针对年轻代的垃圾回收即 Young GC。
    年老代
    在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
    针对年老代的垃圾回收即 Full GC。
    持久代
    用于存放静态类型数据,如 Java Class, Method 等。持久代对垃圾回收没有显著影响。但是有些应用可能动态生成或调用一些Class,例如 Hibernate CGLib 等,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型。
    所以,当一组对象生成时,内存申请过程如下
    JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
    当Eden区空间足够时,内存申请结束。否则执行下一步。
    JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区。
    Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代。
    当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
    Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存,即出现“Out of Memory”。
    OOM(“Out of Memory”)异常一般主要有如下2种原因:
    年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
    这是最常见的情况,产生的原因可能是:设置的内存参数Xmx过小或程序的内存泄露及使用不当问题。
    例如循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存。还有的时候虽然不会报内存溢出,却会使系统不间断的垃圾回收,也无法处理其它请求。这种情况下除了检查程序、打印堆内存等方法排查,还可以借助一些内存分析工具,比如MAT就很不错。
    持久代溢出,表现为:java.lang.OutOfMemoryError:PermGenspace
    通常由于持久代设置过小,动态加载了大量Java类而导致溢出 ,解决办法唯有将参数 -XX:MaxPermSize 调大(一般256m能满足绝大多数应用程序需求)。将部分Java类放到容器共享区(例如Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量的共享类库

  6. 线程问题

     
    pblic class NameList
    {
    private List names = new ArrayList();
    public synchronized void add(String name)
    {
    names.add(name);
    }
    public synchronized void printAll() {
    for (int i = 0; i < names.size(); i++)
    {
    System.out.print(names.get(i) + ””);
    }
    }
    public static void main(String[]args)
    {
    final NameList sl = new NameList();
    for (int i = 0; i < 2; i++)
    {
    new Thread()
    {
    public void run()
    {
    sl.add(“A”);
    sl.add(“B”);
    sl.add(“C”);
    sl.printAll();
    }
    } .start();
    }
    }
    }


    Which two statements are true if this class is compiled and run?
    A An exception may be thrown at runtime.
    B The code may run with no output, without exiting.
    C The code may run with no output, exiting normally(正常地).
    D The code may rum with output “A B A B C C “, then exit.
    E The code may rum with output “A B C A B C A B C “, then exit.
    F The code may ruin with output “A A A B C A B C C “, then exit.
    G The code may ruin with output “A B C A A B C A B C “, then exit.
    【解析】
    在每个线程中都是顺序执行的,所以sl.printAll();必须在前三句执行之后执行,也就是输出的内容必有ABC。
    而线程之间是穿插执行的,所以一个线程执行 sl.printAll();之前可能有另一个线程执行了前三句的前几句。
    E答案相当于线程1顺序执行完然后线程2顺序执行完。
    G答案则是线程1执行完前三句add之后线程2插一脚执行了一句add然后线程1再执行 sl.printAll();输出ABCA。接着线程2顺序执行完输出ABCABC
    输出加起来即为ABCAABCABC。
  7. jvm中的classloader
    一个jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分别各司其职:
    (1)Bootstrap ClassLoader 负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
    (2)Extension ClassLoader 负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class
    (3)App ClassLoader 负责加载当前java应用的classpath中的所有类。
    (others):
    classloader 加载类用的是全盘负责委托机制。 所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入。
    所以,当我们自定义的classlo ader加载成功了 com.company.MyClass以后,MyClass里所有依赖的class都由这个classLoader来加载完成。

  8. java异常处理知识点
    例题:

     
    public void getCustomerInfo() {
    try {
    // do something that may cause an Exception
    } catch (java.io.FileNotFoundException ex) {
    System.out.print(“FileNotFoundException!”);
    } catch (java.io.IOException ex) {
    System.out.print(“IOException!”);
    } catch (java.lang.Exception ex) {
    System.out.print(“Exception!”);
    }
    }


    请选择输出结果:
    A IOException!
    B IOException!Exception!
    C FileNotFoundException!IOException!
    D FileNotFoundException!IOException!Exception!
    答案是:A
    try-catch-finally 规则( 异常处理语句的语法规则 )
    1) 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
    2) 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
    3) catch 块与相应的异常类的类型相关。
    4) 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或 其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
    5) 可嵌套 try-catch-finally 结构。
    6) 在 try-catch-finally 结构中,可重新抛出异常。
    7) 除了下列情况,总将执行 finally 做为结束: JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击 。
    由此可以看出,catch只会匹配一个,因为只要匹配了一个,虚拟机就会使整个语句退出
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值