赞
踩
- 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
- 重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
- 要求两同两小一大原则, 方法名相同,参数类型相同,子类返回类型小于等于父类方法返回类型(即:子返回类型要是父返回类型的子类型或者不变), 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类方法访问权限。
- 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
- 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
大写表示“非”,d表示digit数字。非数字就是\D, w表示word,非单词就是\W
Java语言系统自带有三个类加载器:
一般对于我们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,那岂不是天下大乱了。
作用:在Java里都是用来对比两个对象是否相等一致。
那么equals()既然已经能实现对比的功能了,为什么还要hashCode()呢?因为重写的equals()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。
那么hashCode()既然效率这么高为什么还要equals()呢? 因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,
所以我们可以得出:
- equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。
- hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
所有对于需要大量并且快速的对比的话如果都用equals()去做显然效率太低,所以解决方式是,每当需要
对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equals(),如果equals()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。
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 是如何工作的:
父类静态成员变量 父类静态代码块 子类静态成员变量 子类静态代码块 父类非静态成员变量,父类非静态代码块,父类构造函数,子类非静态成员变量,子类非静态代码块,子类构造函数
前菜:
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻内发生(同时发生)。
正文:
PS:在main方法中直接调用 .run方法只是单线程调用,需要使用 .start方法才是多线程调用。
PS:线程池属于JDK1.5之后加入到jdk中,直接查api使用就行,定义的话百度即可。
1 .继承Thread类,重写run方法,通过.start()调用;
2. 继承Runnable接口,重写run方法,通过.start()调用;
3. Callable 接口相对Runnable接口来说,它可以抛出异常,有返回值,然后需要重写的方法是call()方法。
4. 创建线程池(可以避免线程频繁的创建和销毁,实现重复利用)七个核心参数如下:
相关API有ExecutorService类和Executors类。
springcloud框架举例使用:新建config类,里面新建方法myThreadPoolTaskExecutor返回值为XXXThreadPoolTaskExecutor(springcloud自带的线程池类) ,然后配置核心线程数等参数,在其他要用线程池的地方注入该线程池的bean(名称为myThreadPoolTaskExecutor),调用其execute方法myThreadPoolTaskExecutor.execute( ()->{ //业务逻辑} )
以上图为例
- NEW(创建):该状态描述的是线程已经被new出来,但还未启动。NEW这种状态对于每个线程来说,只可能有一次处于该状态,因为一个线程实例只能够被启动一次。
- RUNNABLE(就绪和运行):如果处于NEW状态的线程调用了start方法,就会处于RUNNABLE状态。但是请注意,线程有可能正在运行,也有可能在等待运行(具体什么时候运行要由线程调度器来安排)!其实就是传统意义的“就绪”和“运行”两个状态的合并。
- BLOCKED(阻塞):阻塞是被动的阻塞,当线程申请一个由其他线程持有的独占资源(比如锁)时就会处于该状态。当线程不再阻塞时,状态会从BLOCKED转为RUNNABLE。
- WAITING(等待):等待是主动的等待,当一个线程执行了某些特定的方法(如:Object.wait() ; Thread.join() )后就会处于这种状态,也就是等待其他线程执行另外一些操作的状态。站在“其他线程”的角度,当“其他线程”完成了这些操作,可以 notify 或者 notifyAll 唤醒一个或者全部等待线程。
- TIMED_WAITING(超时等待):该状态类似于上面的WAITING,只不过WAITING是无限制地等待,而TIMED_WAITING只等待一个特定的时间。时间一到,处于TIMED_WAITING状态的线程就会转为RUNNABLE。如我们最熟悉的sleep(1000)。
- TERMINATED(销毁):当线程执行完毕后会处于该状态。和NEW状态一样,每个线程实例也只可能有一次TERMINATED状态。不管是run方法正常结束还是由于抛出异常而提前终止,都会导致线程处于该种状态。
- volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场景还是更多一些。
- 多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
- volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
- volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
- sleep()方法
- 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。
- sleep()使当前线程进入TIMED_WAITING(超时等待)状态,在指定时间内不会执行。
- wait()方法
- 在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
- 当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
- 唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。
- waite()和notify()必须在synchronized函数或synchronized代码块中进行调用。如果在non-synchronized函数或non-synchronized代码块中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
3.yield方法- 暂停当前正在执行的线程对象,不释放锁。
- yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
- yield()只能使同优先级或更高优先级的线程有执行的机会。
- join方法
- 释放锁
- join()等待该线程终止。
- 等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测
线程安全问题的解决:
PS:
- synchronized关键字加锁的缺陷:
如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行改代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
① 获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
② 线程执行发生异常,此时JVM会让线程自动释放锁。
那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。- 对比
① Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性;Lock是一个类,通过这个类可以实现同步访问。
② Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
- 主要包含RuntimeException等运行时异常和IOException,SQLException等非运行时异常。
- 运行时异常 包括:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
- 运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
- 非运行时异常(检查异常) 包括:RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常
finally块的语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值(如果return使用在基本数据变量上,则finally中对改基本数据变量的修改不会生效,如果作用的是对象是正常对象如Map、List,才生效。),若finally里也有return语句则覆盖try或catch中的return语句直接返回。
- throw:写在方法体中,表示方法一定会抛出一个异常,要么try…catch处理,要么throws抛出。
- throws:写在方法声明之后,表示方法可能抛出异常,调用者需要处理这个异常。
- 两者都是消极的异常处理方式,只是抛出或者可能抛出异常,是不会由函数处理,真正的处理异常由它的上层调用处理。
- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
- 一般认为,java内的基础类型数据传递都是值传递. java中实例对象的传递是引用传递
- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。
参考:https://blog.csdn.net/itchuxuezhe_yang/article/details/89966303
如何选择:
1.字符串是否需要经常修改其内容:需要——StringBuffer和StringBuilder,不需要——String;
2.是否需要线程安全:需要——StringBuffer,不需要StringBuilder。
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;
}
}
}
class SendFactory {
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
大体思路为:工厂A,B,C管理类A1,B1,C1;新建工厂X,X和工厂A,B,C都继承自同一个接口,但是X又是可以创建ABC的一个大工厂。
以 int 和 Integer 举例:
- Integer是int的包装类,int则是java的一种基本数据类型
- Integer变量必须实例化后才能使用,而int变量不需要
- Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
- Integer的默认值是null,int的默认值是0
- 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
- Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
- 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false.
- 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。
- Java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。