Wilder's Blog.

每月一牛客—Java篇(1)

字数统计: 5.3k阅读时长: 20 min
2018/07/15 Share

Java 题目

2018.7.15

  • 多个线程可同时操作一个数据,为了保证该数据的准确性,可将操作数据的部分改为 同步

    • 同步:进程之间的关系不是相互排除临界资源的关系,而是相互依赖的关系。进一步说明就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。
    • 异步:异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
      异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。

  • JSP 四大作用区:page、request、session、application

    • 存储在application对象中的属性可以被同一个WEB应用程序中所有的Servlet 和 Jsp 页面访问。(属性作用范围最大)
    • 存储在session对象中的属性可以被属于同一个会话(浏览器打开直到关闭称为一次会话,且在此期间会话不失效)的所有Servlet 和 Jsp 页面访问
    • 存储在request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问(在有转发的情况下可以跨页面获取属性值),例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。
    • 存储在pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。
  • ServletConfig 接口默认是 GenericServlet 实现的(自己看源码结构吧)

  • 关于JVM内存一些概念的叙述:

    • 程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,是线程隔离的
    • Java 方法执行在内存模型,用于存储局部变量,操作数栈,动态链接,方法出口等信息,是线程隔离的
    • 方法区用于存储JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,是线程共享的(看到静态变量就要想到线程共享啊)
    • 原则上讲,所有的对象都在堆内存上分配,是线程共享的
  • 对象序列化:

    • 要使流可以传输对象,则对象需要实现Serializeable接口,使用ObjectInputStream 和 ObjectOutputStream 便可以实现
    • 使用 transient 修饰的变量不会被初始化
    • 对象序列化的所属类需要实现Serializable 接口
  • 线程安全的集合:vector,stack,hashtable,enumeration,除此之外均是非线程安全的类与接口

    可以这么记忆:喂!SHE! 喂是指 vector,S是指 stack, H是指 hashtable,E是指:Eenumeration

  • 以下JSP代码定义了一个变量,输出这个变量的值:

    <bean:define id=”stringBean” value=”helloWorld”/>

    • 1
      <%=stringBean%>
    • 1
      <bean:write name="stringBean"/>
    • 1
      2
      <%String myBean=(String)pageContext.getAttribute("stringBean",PageContext.PAGE_SCOPE);%>
      <%=myBean%>

2018.7.17

  • 管道(Pipe)通信的叙述

    • 进程对管道进行读操作和写操作都可能被阻塞:当管道中没有信息的时候,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也会自动消失。
    • 管道只支持具有亲缘关系的两个进程之间的通信,比如父进程和子进程
    • 一个管道不可以实现双向传输,匿名管道只能直线单向传输,要想实现双向传输的话可以建立两个管道
    • 管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程消失管道也对应消失
  • HashMap 和 HashTable

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //HashMap的源码
      public class HashMap<K,V>
      extends AbstractMap<K,V>
      implements Map<K,V>, Cloneable, Serializable
      -----------------------------------
      //Hashtable的源码
      public class Hashtable<K,V>
      extends Dictionary<K,V>
      implements Map<K,V>, Cloneable, java.io.Serializable
      ----------------------------------
      很明显,都实现了Map接口
    • 1
      2
      3
      4
      5
      public V put(K key, V value) //HashMap的put方法,没有同步

      public synchronized V put(K key, V value) //Hashtable的put方法
      //当然,Hashtable的其他方法,如get,size,remove等方法,
      //都加了synchronized关键词同步操作
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      //Hashtable的put方法有以下语句块,大伙看了都知道
      // Make sure the value is not null
      if (value == null) {
      throw new NullPointerException();
      }

      //那么,我们再来看下HashMap的put方法中,有如下语句
      //调用某个方法直接把key为null,值为value的键值对插入进去。
      if (key == null)
      return putForNullKey(value);
    • 1
      2
      3
      4
      5
      6
      7
      8
      //以下是Hashtable的方法
      public synchronized boolean contains(Object value)
      public synchronized boolean containsKey(Object key)
      public boolean containsValue(Object value)

      //以下是HashMap中的方法,注意,没有contains方法
      public boolean containsKey(Object key)
      public boolean containsValue(Object value)
  • Java 面向对象基础

    • java为单继承,多实现。可以实现多个接口。
    • 接口允许定义成员,但必须是常量
    • 抽象类和接口类的无法实例化,任何编译器中直接使用new会报错。

2018.7.18

  • Java Socket 获取IP地址

    • 本地IP地址:getLocalAddress()

    • 连接IP地址:getInetAddress()

    • getReuseAddress() 测试是否启用 SO_REUSEADDR

      关于 SO_REUSEADDR : 点我了解

  • Java 基本类型:如果不明确指定,整数型的默认的 int 类型,带小数的默认是 double 类型

  • Java 语言性特点

    • Java 致力于检查程序在编译和运行时的错误

    • Java 能运行虚拟机实现跨平台

    • Java 自己操作内存减少了内存出错的可能性

    • 程序设计语言中,数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,这样的数组称之为“真数组”。

      实现了真数组为Java语言健壮性的特点之一。Java 实现了真数组,避免了覆盖数据的可能(而不是覆盖数据类型的可能)

  • Sql 获取结果集的两种方式:Statement 和 PrepareStatement

    • Statement 创建是不传参的
    • PrepareStatement 是需要传入 sql 语句的
  • JDK提供用于并发编程的同步器

    • Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
    • CyclicBarrier 主要的方法就是一个:await()。await() 方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。
    • 倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。

2018.7.20

  • | 作用域 | 当前类 | 同一包 | 子孙类 | 其他包 |
    | :——– | :— | —- | —- | —- |
    | public | √ | √ | √ | √ |
    | protected | √ | √ | √ | |
    | default | √ | √ | | |
    | private | √ | | | |

  • JDK8开始,接口中可以定义有方法体的方法,方法必须被default和static修饰。除此之外,其他方法都是抽象方法。

    Java的语法是单继承,但是继承可以传递。其实B更准确一点是只能有一个直接父类。

    继承用extends,实现用implements。先继承后实现。

    类是class,接口是interface。

  • 流媒体 技术的一种可以使音频、视频和其他多媒体信息在Internet 及 Internet 上以实时的,无需下载等待的方式进行播放的技术

  • 关于 structs 框架

    • structs 可以进行文件上传
    • structs 基于 MVC 模式,MVC是模型、视图、控制器,是一种设计模式
    • structs 框架让流程结构更清晰
    • structs 需要很多 action 类,会增加文件数目
  • 关于java Object 默认的基本方法

    • equals(Object obj) 指示某个其他对象是否与此对象“相等”
    • Object 对象没有copy() 这个方法
    • wait() 导致当前线程的等待,直到其它线程调用此对象的notify() 方法或者 notifyAll() 方法
  • 在 Web 开发中实现会话跟踪

    ​ 会话跟踪是一种灵活、轻便的机制,它使Web上的状态编程变为可能。
    ​ HTTP是一种无状态协议,每当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。当用户在同一网站的多个页面 之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。当一个客户在多个页面间切换时,服务器会保存该用户的信息。
    有四种方法可以实现会话跟踪技术:URL重写、隐藏表单域、Cookie、Session。

    • 隐藏表单域:,非常适合步需要大量数据存储的会话应用。
    • URL 重写:URL 可以在后面附加参数,和服务器的请求一起发送,这些参数为名字/值对。
    • Cookie:一个 Cookie 是一个小的,已命名数据元素。服务器使用 SET-Cookie 头标将它作为 HTTP响应的一部分传送到客户端,客户端被请求保存 Cookie 值,在对同一服务器的后续请求使用一个Cookie 头标将之返回到服务器。与其它技术比较,Cookie 的一个优点是在浏览器会话结束后,甚至在客户端计算机重启后它仍可以保留其值。
  • Session:使用 setAttribute(String str,Object obj)方法将对象捆绑到一个会话

2018.7.21

  • 类方法
    • 在类方法中不可以用 this 来调用本类的类方法(将 this 理解为对象,而类方法属于类不属于对象,所以类方法前面不能加 this 指针)
    • 在类方法中调用本类的类方法时可以直接调用
    • 在类方法中可以调用实例方法

2018.7.22

  • 面向对象的五大基本原则

    • 单一职责原则(SRP):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的隐申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
    • 开放封闭原则(OCP):软件实体应该是可扩展的,而不可修改的。也就是对外扩展开放,对内修改封闭的。
    • Liskov替换原则(LSP):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替代基类时,才能保证系统在运行期间识别子类,这是保证继承复用的基础。
    • 依赖倒置原则(DIP):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
    • 接口隔离原则(ISP):使用多个小的专门接口,而不要使用一个大的总接口。
  • 内部类实例化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Enclosingone {
    //非静态内部类
    public class InsideOne {}
    //静态内部类
    public static class InsideTwo{}
    }

    class Mytest02{
    public static void main(String args []){
    Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
    Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
    }
    }

2018.7.23

  • 有一个源代码,只包含import java.util.* ; 这一个import语句:

    能访问 Java.util目录下的所有类,但不能访问 Java.util 子目录下的所有类,因为如果 Java.util 里面有个a类,Java.util.regex里面也有一个a类,我们若是要调用a类的方法或属性时,就不知道使用哪个a类

  • 关于枚举类的知识

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    enum AccountType
    {
    SAVING, FIXED, CURRENT;
    private AccountType()
    {
    System.out.println(“It is a account type”);
    }
    }
    class EnumOne
    {
    public static void main(String[]args)
    {
    System.out.println(AccountType.FIXED);
    }

    // 输出结果为:It is a account type
    // It is a account type
    // It is a account type
    // FIXED
    }

    枚举类在后台实现时,实际上是转化为一个继承了 java.lang.Enum类的实体类,原先的枚举类型变成对应的实体类型。上述例子中 AccountType 将会转换成Class AccountType,并且会生成新的构造函数,假如原来有构造函数,则在此基础上添加两个参数,生成新的构造函数。所以上述例子的构造函数将变为:

    1
    2
    3
    4
    pricate AccountType(String s, int i){
    super(s, i);
    System.out.println(“It is a account type”);
    }

    我们看到Enum 类的源码,这两个参数分别代表 枚举变量和名称 和 这个枚举变量在声明中的位置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * Sole constructor. Programmers cannot invoke this constructor.
    * It is for use by code emitted by the compiler in response to
    * enum type declarations.
    *
    * @param name - The name of this enum constant, which is the identifier
    * used to declare it.
    * @param ordinal - The ordinal of this enumeration constant (its position
    * in the enum declaration, where the initial constant is assigned
    * an ordinal of zero).
    */
    protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
    }

    在这个类中,会添加若干个字段来代表具体的枚举类型:

    1
    2
    3
    public static final AccountType SAVING ;
    public static final AccountType FIXED ;
    public static final AccountType CURRENT ;

    而且还会添加一段 static 代码段:

    1
    2
    3
    4
    5
    6
    7
    static {
    SAVING = new AccountType("SAVING", 0);
    FIXED = new AccountType("FIXED", 0);
    CURRENT = new AccountType("CURRENT", 0);

    $VALUES = new AccoutType[]{SAVING, FIXED, CURRENT}
    }

    以此来初始化枚举中的每个具体类型。(并将所有具体类型放到一个$VALUE数组中,以便用序号访问具体类型)

    在初始化过程中new AccountType构造函数被调用了三次,所以Enum中定义的构造函数中的打印代码被执行了3遍。

  • 以下代码的输出结果是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class B
{
public static B t1 = new B();
public static B t2 = new B();
{
System.out.println("构造块");
}
static
{
System.out.println("静态块");
}
public static void main(String[] args)
{
B t = new B();
}
}

结果为:构造块 构造块 静态块 构造块

开始时JVM加载B.class,对所有的静态成员进行声明,t1 t2被初始化为默认值,为null,又因为t1 t2需要被显式初始化,所以对t1进行显式初始化,初始化代码块→构造函数(没有就是调用默认的构造函数),咦!静态代码块咋不初始化?因为在开始时已经对static部分进行了初始化,虽然只对static变量进行了初始化,但在初始化t1时也不会再执行static块了,因为JVM认为这是第二次加载类B了,所以static会在t1初始化时被忽略掉,所以直接初始化非static部分,也就是构造块部分(输出’’构造块’’)接着构造函数(无输出)。接着对t2进行初始化过程同t1相同(输出’构造块’),此时就对所有的static变量都完成了初始化,接着就执行static块部分(输出’静态块’),接着执行,main方法,同样也,new了对象,调用构造函数输出(’构造块’)

2018.7.25

  • 使用Float创建的三种方法
1
2
3
4
5
6
//自动装箱拆箱方法
Float f = 1.6f; //注意浮点数默认是double类型,所以 Float f = 1.6
//两种构造方法
Float f1 = new Float(1.6);
Float f2 = new Float(1.6f);
//Float 类有两种构造方式,分别是 Float(float f) 和 Float(double d)
  • Servlet 和 CGI 的描述
    • 对比一:当用户浏览器发出一个Http/CGI的请求,或者说调用一个CGI程序的时候,服务器端就要新启用一个进程 (而且是每次都要调用),调用CGI程序越多(特别是访问量高的时候),就要消耗系统越多的处理时间,只剩下越来越少的系统资源,对于用户来说,只能是漫长的等待服务器端的返回页面了,这对于电子商务激烈发展的今天来说,不能不说是一种技术上的遗憾。而Servlet充分发挥了服务器端的资源并高效的利用。每次调用Servlet时并不是新启用一个进程 **,而是在一个Web服务器的进程敏感词享和分离线程,而线程最大的好处在于可以共享一个数据源,使系统资源被有效利用。
    • 对比二:传统的CGI程序,不具备平台无关性特征,系统环境发生变化,CGI程序就要瘫痪,而Servlet具备Java的平台无关性,在系统开发过程中保持了系统的可扩展性、高效性。
    • 对比三:传统技术中,一般大都为二层的系统架构,即Web服务器+数据库服务器,导致网站访问量大的时候,无法克服CGI程序与数据库建立连接时速度慢的瓶颈,从而死机、数据库死锁现象频繁发生。而我们的Servlet有连接池的概念,它可以利用多线程的优点,在系统缓存中事先建立好若干与数据库的连接,到时候若想和数据库打交道可以随时跟系统”要”一个连接即可,反应速度可想而知。
  • 方法重写的两同两小一大规则
    • 方法名相同,参数类型相同
    • 子类返回类型小于等于父类方法返回类型
    • 子类抛出异常小于等于父类方法抛出异常
    • 子类访问权限大于等于父类方法访问权限

2018.7.26

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class C {
    C() {
    System.out.print("C");
    }
    }

    class A {
    C c = new C();

    A() {
    this("A");
    System.out.print("A");
    }

    A(String s) {
    System.out.print(s);
    }
    }

    class Test extends A {
    Test() {
    super("B");
    System.out.print("B");
    }

    public static void main(String[] args) {
    new Test();
    }
    }

    初始化过程:

    (1)首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;

    (2)然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;

    (3)其次,初始化父类中的普通成员变量和代码块,再执行父类的构造方法;

    (4)最后,初始化子类的普通成员变量和代码块,再执行子类的构造方法

    所以可以看到,main方法运行的时候先找初始化A类的普通成员变量和代码块(父类和子类都没有静态成员变量和代码块),也就是运行了C c = new C( )。

    之后初始化构造方法A(String B),最后再打印出B,所以打印出“CBB”

  • 可以把任何一种数据类型的变量赋给 Object 类型的变量

  • 在 Java 中,constructor 必须与 class 同名,方法也可以与 class 同名

2018.7.27

  • Vector相当于一个线程安全的List

    HashMap是非线程安全的,其对应的线程安全类是HashTable

    Arraylist是非线程安全的,其对应的线程安全类是Vector

    StringBuffer是线程安全的,相当于一个线程安全的StringBuilder

    Properties实现了Map接口,是线程安全的

2018.7.28

  • Object 类的方法

  • 静态内部类不可以直接访问外围类的非静态数据,而非静态内部类可以直接访问外围类的数据,包括私有数据。

2018.7.30

  • Java 异常机制

    运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

    运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
    非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

CATALOG
  1. 1. Java 题目
    1. 1.1. 2018.7.15
    2. 1.2. 2018.7.17
    3. 1.3. 2018.7.18
    4. 1.4. 2018.7.20
    5. 1.5. 2018.7.21
    6. 1.6. 2018.7.22
    7. 1.7. 2018.7.23
    8. 1.8. 2018.7.25
    9. 1.9. 2018.7.26
    10. 1.10. 2018.7.27
    11. 1.11. 2018.7.28
    12. 1.12. 2018.7.30