当前位置:   article > 正文

Java基础知识(牛客刷题)记录..(持续输出中)_java刷题

java刷题

1. Java 重写(Override)与重载(Overload)

  • 重写(Override)
  • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
  • 重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
  • 要求两同两小一大原则, 方法名相同,参数类型相同,子类返回类型小于等于父类方法返回类型(即:子返回类型要是父返回类型的子类型或者不变), 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类方法访问权限。
  • 重载(Overload)
  • 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
  • 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
    最常用的地方就是构造器的重载。

    重载规则:
  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

2. 正则表达式

大写表示“非”,d表示digit数字。非数字就是\D, w表示word,非单词就是\W

3. 抽象类(abstract)和接口

抽象类

  1. 最好不要有private因为私有和抽象放在一起,子类如果想重写父类的私有方法根本继承不过来,也就无法重写
  2. 抽象类中可以有非抽象方法,甚至抽象类中可以都是非抽象的方法(但是后者没有啥意义)
  3. 抽象类中是可以定义静态方法的,但是不可以定义静态抽象方法

接口

  1. 接口允许定义成员,但必须是常量,因为定义的变量,在编译的时候都会默认加上public static final。(final修饰的属性必须赋初始值)
  2. 在接口中的方法,永远都被public abstract来修饰。
  3. 接口中定义的方法都需要有实现类来实现,如果实现类不能实现接口中的所有方法则实现类定义为抽象类。
  4. JDK1.8之后接口可以写默认方法:default void run(){ }但是默认方法只可以由实现类的对象调用。
  5. JDK1.8之后接口可以写静态方法,但是只能用接口名调用。
  6. JDK1.9之后接口可以有私有方法,私有方法只能被其他私有方法或者默认方法调用。

比较

  1. 抽象类中可以有静态方法,接口中也可以有。
  2. 抽象类和接口类无法实例化(忽略匿名内部类),任何编译器中直接使用new会报错。
  3. 抽象类可以有构造方法,接口中不能有构造方法。
  4. 接口与抽象的区别就在于抽象类可以提供某些方法的部分实现,而接口则不可以,这也大概是抽象类唯一的优点。

4. JVM

Java语言系统自带有三个类加载器:

  1. Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
  2. Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
  3. Application ClassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。

       一般对于我们java程序员来说,类的加载使用的是双亲委派模型,即当一个类需要加载时,会将类传给Application ClassLoader,但是Application ClassLoader并不会加载,不管它是否能加载,而是传给它的"父类" Extension ClassLoader,Extension ClassLoader同样不会加载,同样传给 Bootstrap ClassLoader(注意不是我们常说的那种父类,但是可以这样理解),这时Bootstrap ClassLoader会判断它是否能加载,能加载就直接加载了,不能加载就传给Extension ClassLoader,Extension ClassLoader同样的判断是否能加载,能加载就直接加载,不能加载就传给Application ClassLoader,然后Application ClassLoader也判断能否加载,如果还是不能加载应该就是报ClassNotFoundException了。这就是双亲委托模型的简单理解了。
       对于上面的"父类"为什么要打引号,因为它们并不是真的像java中继承的关系,而是组合的关系,即在"子类"中存在一个成员变量指向"父类"的引用。

双亲委派有啥好处:它使得类有了层次的划分。就拿java.lang.Object来说,你加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中。这样如果有不法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护。因为这个机制使得系统中只会出现一个java.lang.Object,不会乱套了。你想想如果我们JVM里面有两个Object,那岂不是天下大乱了。

5.面向对象的特征有哪些方面

  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类),继承让变化中的软件系统有了一定的延续性。
  • 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
  • 多态:父类引用指向子类对象。如果我们定义了一个指向子类的父类引用类型,那么它除了能够引用父类的共性外,还可以使用子类强大的功能。但是父类类型的引用可以调用父类中定义的所有属性和方法,对于存在与子类中的方法和属性它就望尘莫及了。

6. hashCode()方法和equals()方法

作用:在Java里都是用来对比两个对象是否相等一致。

那么equals()既然已经能实现对比的功能了,为什么还要hashCode()呢?因为重写的equals()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。
那么hashCode()既然效率这么高为什么还要equals()呢? 因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,
所以我们可以得出:

  1. equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。
  2. hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。

所有对于需要大量并且快速的对比的话如果都用equals()去做显然效率太低,所以解决方式是,每当需要
对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equals(),如果equals()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

7.集合——List,Set以及Map

Java集合Connection和Map特性、继承关系详解

8. Servlet

Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。

  1. 加载:容器通过类加载器使用servlet类对应的文件加载servlet
  2. 创建:通过调用servlet构造函数创建一个servlet对象
  3. 初始化:调用init方法初始化
  4. 处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
  5. 卸载:调用destroy方法让servlet自己释放其占用的资源

init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。

service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。

destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

Tomcat 与 Servlet 是如何工作的:

  1. Web Client 向Servlet容器(Tomcat)发出Http请求;
  2. Servlet容器接收Web Client的请求;
  3. Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中;
  4. Servlet容器创建一个HttpResponse对象;
  5. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet 对象;
  6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息;
  7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据;
  8. Servlet容器把HttpServlet的响应结果传给Web Client。

9. Java初始化的加载顺序为:

父类静态成员变量 父类静态代码块 子类静态成员变量 子类静态代码块 父类非静态成员变量,父类非静态代码块,父类构造函数,子类非静态成员变量,子类非静态代码块,子类构造函数

10. Java多线程

前菜:

  • 10.1 并发与并行

并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻内发生(同时发生)。

并发与并行

  • 10.2 线程概念:

在这里插入图片描述
正文:

  • 10.3 多线程原理_多线程内存图解

在这里插入图片描述

PS:在main方法中直接调用 .run方法只是单线程调用,需要使用 .start方法才是多线程调用。
PS:线程池属于JDK1.5之后加入到jdk中,直接查api使用就行,定义的话百度即可。

  • 10.4 Java中实现多线程的多种方法:

    1 .继承Thread类,重写run方法,通过.start()调用;
    2. 继承Runnable接口,重写run方法,通过.start()调用;
    3. Callable 接口相对Runnable接口来说,它可以抛出异常,有返回值,然后需要重写的方法是call()方法。
    4. 创建线程池(可以避免线程频繁的创建和销毁,实现重复利用)七个核心参数如下:

    • 4.1 corePoolSize:核心池的大小。 是线程池中的一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut
    • 4.2 maximumPoolSize:最大线程数。线程池能够容纳同时执行的最大线程数,此值大于等于1。一个任务被提交到线程池以后,首先会找有没有空闲并且存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会放到工作队列中,直到工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。工作队列满,且线程数等于最大线程数,此时再提交任务则会调用拒绝策略。
    • 4.3 keepAliveTime:当线程空闲时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。默认情况下:只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,直到线程中的线程数不大于corepoolSIze.
    • 4.4 unit 空闲线程存活时间单位:keepAliveTime的计量单位,时分秒
    • 4.5 workQueue 工作队列
    • 4.6 threadFactory 线程工厂:创建一个线程工厂用来创建线程,可以用来设定线程名、是否为daemon线程等等
    • 4.7 handler 拒绝策略
      • AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。(默认这种)
      • DiscardPolicy:丢弃任务,但是不抛出异常
      • DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 。也就是当任务被拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务从队尾添加进去,等待执行。
      • CallerRunsPolicy:谁调用,谁处理。由调用线程(即提交任务给线程池的线程)处理该任务,如果线程池已经被shutdown则直接丢弃

    相关API有ExecutorService类和Executors类。在这里插入图片描述
    springcloud框架举例使用:新建config类,里面新建方法myThreadPoolTaskExecutor返回值为XXXThreadPoolTaskExecutor(springcloud自带的线程池类) ,然后配置核心线程数等参数,在其他要用线程池的地方注入该线程池的bean(名称为myThreadPoolTaskExecutor),调用其execute方法myThreadPoolTaskExecutor.execute( ()->{ //业务逻辑} )

  • 10.5 线程状态(从java代码角度讲有六种)

状态图

以上图为例

  1. NEW(创建):该状态描述的是线程已经被new出来,但还未启动。NEW这种状态对于每个线程来说,只可能有一次处于该状态,因为一个线程实例只能够被启动一次。
  2. RUNNABLE(就绪和运行):如果处于NEW状态的线程调用了start方法,就会处于RUNNABLE状态。但是请注意,线程有可能正在运行,也有可能在等待运行(具体什么时候运行要由线程调度器来安排)!其实就是传统意义的“就绪”和“运行”两个状态的合并。
  3. BLOCKED(阻塞):阻塞是被动的阻塞,当线程申请一个由其他线程持有的独占资源(比如锁)时就会处于该状态。当线程不再阻塞时,状态会从BLOCKED转为RUNNABLE。
  4. WAITING(等待):等待是主动的等待,当一个线程执行了某些特定的方法(如:Object.wait() ; Thread.join() )后就会处于这种状态,也就是等待其他线程执行另外一些操作的状态。站在“其他线程”的角度,当“其他线程”完成了这些操作,可以 notify 或者 notifyAll 唤醒一个或者全部等待线程。
  5. TIMED_WAITING(超时等待):该状态类似于上面的WAITING,只不过WAITING是无限制地等待,而TIMED_WAITING只等待一个特定的时间。时间一到,处于TIMED_WAITING状态的线程就会转为RUNNABLE。如我们最熟悉的sleep(1000)。
  6. TERMINATED(销毁):当线程执行完毕后会处于该状态。和NEW状态一样,每个线程实例也只可能有一次TERMINATED状态。不管是run方法正常结束还是由于抛出异常而提前终止,都会导致线程处于该种状态。
  • 10.6 synchronized关键字和volatile关键字比较

  • volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场景还是更多一些。
  • 多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
  • volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
  • 10.7 四个方法

  1. sleep()方法
  • 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。
  • sleep()使当前线程进入TIMED_WAITING(超时等待)状态,在指定时间内不会执行。

  1. wait()方法
  • 在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
  • 当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
  • 唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。
  • waite()和notify()必须在synchronized函数或synchronized代码块中进行调用。如果在non-synchronized函数或non-synchronized代码块中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

    3.yield方法
  • 暂停当前正在执行的线程对象,不释放锁。
  • yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
  • yield()只能使同优先级或更高优先级的线程有执行的机会。

  1. join方法
  • 释放锁
  • join()等待该线程终止。
  • 等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测
  • 10.8 线程安全问题(锁的应用)

在这里插入图片描述

线程安全问题的解决:

  1. 第一种方法:使用同步代码块技术
        格式:
        synchronized(锁对象) {
        可能会出现线程安全问题的代码(访问共享数据的代码)
        注意:
        1、通过代码块的锁对象,可以是任意的对象
        2、必须保证多个线程使用的锁对象是同一个
        3、锁对象的作用是把同步代码快锁住,只允许一个线程在同步代码块执行
  2. 第二种方法:同步方法
        使用步骤:
        1、把访问了共享数据的代码抽取出来,放到一个方法中
        2、在该方法上添加 synchronized 修饰符
        格式:
        修饰符 synchronized 返回值类型 方法名称(参数列表) {
         method body
        }
        然后调用这个方法就可以了。
  3. 第三种方法:Lock接口
        java.util.concurrent.locks.Lock接口
        Lock接口中的方法:
        void lock():获取锁
        void unlock():释放锁
        Lock接口的一个实现类
        java.util.concurrent.locks.ReentrantLock 接口实现类
        使用方法:
        1、在Runable实现类的成员变量创建一个ReentrantLock对象
        2、在可能产生线程安全问题的代码前该对象调用lock方法获取锁
        3、在可能产生线程安全问题的代码后该对象调用unlock方法获取锁

PS:

  1. synchronized关键字加锁的缺陷:
    如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行改代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
    ① 获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
    ② 线程执行发生异常,此时JVM会让线程自动释放锁。
    那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
  2. 对比
    ① Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性;Lock是一个类,通过这个类可以实现同步访问。
    ② Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

11.Exception(异常)

  1. 主要包含RuntimeException等运行时异常和IOException,SQLException等非运行时异常。
  2. 运行时异常 包括:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
  • 运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
  1. 非运行时异常(检查异常) 包括:RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常
  • finally与return的执行顺序

finally块的语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值(如果return使用在基本数据变量上,则finally中对改基本数据变量的修改不会生效,如果作用的是对象是正常对象如Map、List,才生效。),若finally里也有return语句则覆盖try或catch中的return语句直接返回。

  • throw和throws
  1. throw:写在方法体中,表示方法一定会抛出一个异常,要么try…catch处理,要么throws抛出。
  2. throws:写在方法声明之后,表示方法可能抛出异常,调用者需要处理这个异常。
  3. 两者都是消极的异常处理方式,只是抛出或者可能抛出异常,是不会由函数处理,真正的处理异常由它的上层调用处理。

12.什么是值传递和引用传递

  • 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
  • 一般认为,java内的基础类型数据传递都是值传递. java中实例对象的传递是引用传递
  • 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。

13.String ,StringBuffer , StringBuilder

参考:https://blog.csdn.net/itchuxuezhe_yang/article/details/89966303

  • String:是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。
  • StringBuffer:线程安全,可变。
  • StringBuilder:相对上面那个,线程不安全,但是效率高,其他都差不多。

如何选择:
1.字符串是否需要经常修改其内容:需要——StringBuffer和StringBuilder,不需要——String;
2.是否需要线程安全:需要——StringBuffer,不需要StringBuilder。

14.常用设计模式(待完善)

  • 设计模式七大原则
    1. 单一职责原则:一个设计元素只做一件事。
    2. 开闭原则:对修改关闭;对扩展开放。
    3. 里氏替换原则:只要有父类出现的地方,都可以用子类来替代。
    4. 接口隔离原则:一个类对另外一个类的依赖是建立在最小的接口上。
    5. 依赖倒置原则:对接口编程的意思是说,应当使用接口和抽象类进行变量的类型声明,参量的类型声明,方法的返还类型声明,以及数据类型的转换等.不要针对实现编程的意思就是说,不应当使用具体类进行变量的类型声明,参量类型声明,方法的返还类型声明,以及数据类型的转换等。要保证做到这一点,一个具体的类应该只实现接口和抽象类中声明过的方法,而不应当给出多余的方法。
    6. 迪米特法则:对其他对象有尽可能少的了解.,只与你直接的朋友们(类中的成员变量,方法参数,返回值)通信,不要跟"陌生人(方法中的成员变量)"说话。
    7. 合成复用原则:尽量使用合成/聚合,尽量不要使用继承。
  • 常用设计模式
    参考(细节都在这里面!):https://blog.csdn.net/yubujian_l/article/details/81455524.
  1. 单例模式:
    饿汉式:static成员变量(唯一对象),并且new出来了。
    懒汉式:static成员变量(唯一对象),直接赋null,用的时候在方法里面再去new。
  2. 工厂模式:
  • (1)简单工厂模式:
public class FactoryPattern {
    public static Sender produce(String str) {
        if ("mail".equals(str)) {
            return new MailSender();
        } else if ("sms".equals(str)) {
            return new SmsSender();
        } else {
            System.out.println("输入错误...");
            return null;
        }
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • (2)工厂方法模式:(还可以把方法设置成静态的,就可以类名直接调用了)
class SendFactory {
    public Sender produceMail() {
        return new MailSender();
    }

    public Sender produceSms() {
        return new SmsSender();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • (3)抽象工厂模式(简单工厂模式+工厂方法模式 的一种整合):

大体思路为:工厂A,B,C管理类A1,B1,C1;新建工厂X,X和工厂A,B,C都继承自同一个接口,但是X又是可以创建ABC的一个大工厂。

  1. 建造者模式:
    有个产品类Computer;有个Builder抽象类(里面是组装电脑的步骤1,2,3…),有很多Builder的实现类,重写了步骤方法1,2,3…,最后有个Director类,利用多态,创建Builder,然后依次调用方法1,2.3…(因为多态嘛,到时候传过来的Builder肯定是它的一个实现类)

15.自动拆箱装箱

以 int 和 Integer 举例:

  • 区别:
  1. Integer是int的包装类,int则是java的一种基本数据类型
  2. Integer变量必须实例化后才能使用,而int变量不需要
  3. Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
  4. Integer的默认值是null,int的默认值是0
  • 比较:
  1. 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
  2. Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
  3. 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false.
  4. 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。
  5. Java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
  • 附带一点小知识(有关数字字符串和Integer类型之间的转换)

1. Integer.parseInt(String s); //字符串转数字
2.Interger i = 123; i.toSting(); //数字123转为字符串123
3.Integer.valueOf("123");//字符串123转为数字123
4.String.valueOf(13); //数字13转为字符串13

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/494862
推荐阅读
相关标签
  

闽ICP备14008679号