重载(overload):是一个类中多态性的一种表现,只要方法名 一致 ,但要求每个方法具有不同的参数的类型或参数的个数,方法的返回类型、修饰符可以相同,也可不同。这里需要注意的是,函数重载不能基于返回值类型
重写(overriding):
1.“两小” :子类方法的返回值类型、抛出的异常 均应小于等于父类方法的
2.“两同”:子类方法的方法名、参数列表均应该与父类方法相同
3.“一大”:子类方法的访问权限应该大于等于父类方法的访问权限
隐藏:
隐藏是针对父类中的成员变量和静态方法而言 。当子类声明了与父类相同的变量名的变量时就实现了对父类成员变量的隐藏。当子类声明了与父类的静态成员方法中相同方法名,相同参数列表和相同返回类型的方法,则实现了对父类静态方法的隐藏
public可以被当前类,子类,包,其他包,访问,
protected 可以被当前类,子类,包访问
default可以被可以被当前类,包内访问;
private只能被当前类访问
这里要注意一点:private的属性和方法子类是可以继承的,但是不能调用!
abstract, assert, boolean, break, byte, case, catch, char, class, const(保留关键字), continue, default, do, double, else, enum, extends, final, finally, float, for, goto(保留关键字), if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, while
byValue, cast, false, future, generic, inner, operator, outer, rest, true, var, goto (保留关键字) , const (保留关键字) , null
与 构造函数 相反,当对象结束其 生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)
Java语言中,对于变量,常量,函数,语句块也有名字,我们统统称之为Java标识符。
标识符可以包括这4种字符:字母、下划线、$、数字;开头不能是数字;不能是关键字
简单的可以记为:数字划钱
注意:不能有空格!
this表示当前对象,也就是当前类对象,super表示当前类的父类。
举例:你定义一个新的类:A,这个A继承了类B,也就是说B是A的父类。那么如果A中 有个方法:aa();B中也有个方法: aa();
那么在A 中用this.aa()调用的就是A中定义的方法,而super.aa()调用的就是A的父类B中定义的方法aa();
注意:
JDK常用的包主要有以下几种
final修饰类、方法、属性!不能修饰抽象类,因为抽象类一般都是需要被继承的,final修饰后就不能继承了。
final修饰的方法不能被重写(覆盖)而不是重载!
final修饰属性,此属性就是一个常量,不能被再次赋值!
web容器:给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使 JSP,SERVLET直接更容器中的环境变量接**互,不必关注其它系统问题。主要有WEB服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE等。该容器提供的接口严格遵守J2EE规范中的WEB APPLICATION 标准。我们把遵守以上标准的WEB服务器就叫做J2EE中的WEB容器。
EJB容器:Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
JNDI:(Java Naming & Directory Interface)JAVA命名目录服务。主要提供的功能是:提供一个目录系,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:(Java Message Service)JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JTA:(Java Transaction API)JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。
JAF:(Java Action FrameWork)JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
RMI/IIOP:(Remote Method Invocation /internet对象请求中介协议)他们主要用于通过远程调用服务。例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
java中源文件的后缀为.java,
经过javac.exe编译后生成字节码文件,后缀为.class,
再经过java.exe编译为可执行文件,后缀为.exe。
Java程序经编译后会产生byte code
⑴静态方法中不能引用非静态方法和非静态属性—理由很简单,因为静态方法直接可以不经过实例化就可以使用。但是非静态方法必须借助一个实例才能使用。静态方法中没有this指针
静态方法可以重载
static的方法在装载class得时候首先完成,比 构造方法早,此时非static得和方法还没有完成初始化,所以不能调用。
方法是static静态方法,直接使用"类.方法"即可,因为静态方法使用不依赖对象是否被创建。
⑵静态代码块优先于主方法,且只执行一次
⑶只有类才存在静态的变量 ,非静态成员函数中可以调用静态成员,方法只能对静态变量的操作 不能在方法内试图定义静态变量
⑷执行顺序:
其中涉及:静态初始化代码块、构造代码块、构造方法
当涉及到继承时,按照如下顺序执行:
1、执行父类的静态代码块
static {
System.out.println("static A");
}
输出:static A
2、执行子类的静态代码块
static {
System.out.println("static B");
}
输出:static B
3、执行父类的构造代码块
{
System.out.println("I’m A class");
}
输出:I'm A class
4、执行父类的构造函数
public HelloA() {
}
输出:无
5、执行子类的构造代码块
{
System.out.println("I’m B class");
}
输出:I'm B class
6、执行子类的构造函数
public HelloB() {
}
输出:无
Java表达式转型规则由低到高转换:
byte→short(char)→int→long→float→double
记住:
byte b1=1,b2=2,b3,b6,b8;
final byte b4=4,b5=6,b7;
b3=(b1+b2); //b3=(b1+b2);自动转为int,所以正确写法为b3=(byte)(b1+b2);或者将b3定义为int;
b6=b4+b5; //b6=b4+b5;b4、b5为final类型,不会自动提升,所以和的类型视左边变量类型而定,即b6可以是任意数值类型
b8=(b1+b4); //b8=(b1+b4);虽然b4不会自动提升,但b1仍会自动提升,所以结果需要强转,b8=(byte)(b1+b4);
b7=(b2+b5); //b7=(b2+b5); 同上。同时注意b7是final修饰,即只可赋值一次,便不可再改变
基本类型(属于原生类)只能保存一些常量数据,引用类型除了可以保存数据,还能提供操作这些数据的功能;
为了操作基本类型的数据,java也对它们进行了封装, 得到八个类,就是java中的基本类型的封装类;他们分别是:
八种基本类型: byte short int long float double char boolean
对应的包装类 : Byte Short Integer Long Float Double Character Boolean
引用类型包括类、接口和数组类型以及特殊的null类型。
引用数据类型:变量名指向存数据对象的内存地址,即变量名指向hash值
JDK:JAVA语言开发工具包,在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib合起来就称为jre。
JRE:包含JVM标准实现和JAVA核心类库,JRE是JAVA运行环境而不是开发环境,不包含任何开发工具(如编译器和调试器)
JVM:JAVA虚拟机,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
JDK是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。JRE是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。JVM是整个java实现跨平台的最核心的部分,能够运行以Java语言写的程序。
当我们安装完JDK后会出现两个JRE,这是为什么呢?
一个jre是放在jdk文件夹下面的,这个是开发使用的jre.
一个是供系统其他java应用使用的jre使用的。如果我们不进行开发只运行JAVA程序的话,就不需要安装JDK
成员变量有初始值,局部变量没有初始值,final修饰的变量一定要有初始值
过滤器,监听器,拦截器的区别
JAVA序列化:把JAVA对象转化为字节序列
JAVA反序列化 :把字节序列转化为JAVA对象
序列化的作用:
⑴实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
⑵利用序列化实现远程通信,即在网络上传送对象的字节序列
序列化注意事项:
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化,反之就是自动拆箱。
public static void main(String[] args) {
int a=257;
Integer b=257;
System.out.println(a==b); //同类型基本类型和封装类型比,转化为基本类型 true
System.out.println(b.equals(a)); // true
System.out.println(b.equals(257.0)); // false
}
例子1:
//定义一个short类型变量
short sValue = 5;
//表达式中的sValue将自动提升到int类型,则右边的表达式类型为int
//将一个int类型赋给short类型的变量将发生错误。
sValue = sValue - 2;
例子2:
byte b1=1,b2=2,b3,b6;
final byte b4=4,b5=6;
b6=b4+b5;
b3=(b1+b2);
System.out.println(b3+b6);
语句:b3=b1+b2编译出错
被final修饰的变量是常量,这里的b6=b4+b5可以看成是b6=10;在编译时就已经变为b6=10了
而b1和b2是byte类型,java中进行计算时候将他们提升为int类型,再进行计算,b1+b2计算后已经是int类型,赋值给b3,b3是byte类型,类型不匹配,编译不会通过,需要进行强制转换。
servlet就是一个Java接口,本质上是一套处理网络请求的规范。
实现了servlet的类也并不能直接和客户端打交道,请求需要通过servlet容器(例如tomcat)来和客户端打交道。
web容器加载servlet,生命周期开始,调用init()方法初始化,然后service实现,根据请求不同调用doGet或doPost方法。结束服务,web容器调用destroy()方法
/**
* @author bincai, [email protected]
* @date Jul 19 , 2018
*/
public class Test {
public static void main(String[] args) {
try {
test();
} catch (RuntimeException e) {
System.out.println("捕获下级方法的运行时异常");
}
}
private static void test() {
test2();
}
private static void test2() {
throw new RuntimeException("2323");
}
}
输出:
捕获下级方法的运行时异常
public static void main(String[] args) {
try {
System.out.println(1 / 0);
} finally {
System.out.println(2);
}
}
输出:
2
Exception in thread "main" java.lang.ArithmeticException: / by zero
at testswitch.TestTry.main(TestTry.java:11)
一个接口,多种实现,通过抽象化来提高程序的扩展性。
方法调用是依照方法的符号引用得到具体内存引用的过程,调用类的方法在类加载时间就直接可以转化为具体内存引用,这个是静态绑定,而调用对象方法则是动态绑定,最后得到真正的方法引用,完成调用。
关于hashCode方法和equals方法,一致的约定是
为什么重写equals的时候要重写hashcode
如果2个对象通过equals调用后返回是true,那么这个2个对象的hashCode方法也必须返回同样的int型散列码。而且如果不重写hashcode,在hashmap以对象做key时也会出问题。
①例题1:
假设有以下代码
String s=”hello”;
String t=”hello”;
Char c[] ={‘h’,’e’,’l’,’l’,o’’};
下列选项中返回false的语句是: B
s.equals(t);
t.equals(c);
s==t;
t.equals(new String(“hello”));
这里s==t返回的是true,为什么呢?因为这里有个字符串缓存的机制,建立string t="hello"时会去string缓存池中找相同的对象,如果想让为fasle,可以new 一个新的string
②例题2:
public class StringDemo{
private static final String MESSAGE="taobao";
public static void main(String [] args) {
String a ="tao"+"bao";
String b="tao";
String c="bao";
System.out.println(a==MESSAGE); //true
System.out.println((b+c)==MESSAGE); //false
}
}
数组是引用类型,不属于原生类,可以看成是一种对象,一旦大小指定就不可以进行改变了。
int a[]; //声明未初始化
a = new int [10]; // 定义占用空间大小(10个int)
int a[] = new int [10]; //声明并定义大小(即分配了指定大小的空间)
int a[] = {1,2,3}; // 声明并初始化,占用空间大小是3个int。
float a[][]=new float[3][];
float []b=new float[6];
int []c=new int[3];
另外我们要知道:
String x[][]=new String[3][2],,实际上x为:
{{“A某些String”,"B 某些String "}
{"C 某些String ","D 某些String "}
{"E 某些String ","F 某些String "} }
x.length取的是最外层括号包含的元素个数,因此x.length=3;
x[0][1]取的是第一个最外层括号包含元素内的第二个元素,即x[0][1]= "B 某些String "
先来个效率对比:System.arraycopy > clone > Arrays.copyOf > for循环,我们一般用最快的,其实前三个效率差不多。
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public static void arraycopy(Object src,
int srcPos,
Object dest,
int destPos,
int length)
其实在JAVA8可以通过lambda来更简单的写比较器。
若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序,一般要重写compareTo方法。
外部比较器,位于java.util包下,Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。一般要重写compare方法。
这里我们要看方法内是否修改了堆内存的内容,堆内存内容并没有变化则原变量的内容也没有发生变化。
package testSpringMVC;
//蔡彬
public class JstackCase {
public static void main(String[] args) {
girl a=new girl();
a.setAge(10);
girl b=new girl();
b.setAge(20);
test0(a,b);
System.out.println(a.getAge()); // 200
System.out.println(b.getAge()); // 100
girl c=new girl();
c.setAge(10);
girl d=new girl();
d.setAge(20);
test(c,d);
System.out.println(c.getAge()); // 10 因为在方法内并没有修改堆的内容
System.out.println(d.getAge()); // 20
}
private static void test0(girl a, girl b) {
// TODO Auto-generated method stub
a.setAge(200);
b.setAge(100);
}
private static void test(girl c, girl d) {
// TODO Auto-generated method stub
c=d;
}
}
整个类加载过程中,除了在加载阶段用户应用程序可以自定义类加载器参与之外,其余所有的动作完全由虚拟机主导和控制,
双亲委托机制:
Java的类加载使用双亲委派模式,即一个类加载器在加载类时,先把这个请求委托给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委托,直到顶层的启动类加载器。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。
为什么采用双亲委托机制:
一个可以避免类的重复加载,另外也避免了java的核心API被篡改
打破双亲委托机制:
不仅要继承ClassLoader类,还要重写loadClass和findClass方法。
验证的目的是为了确保Class文件中的字节流包含的信息符合当前虚拟机的要求。
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
public static int value = 3;
准备阶段只会将它赋值为0,直到初始化阶段才会将它赋值为3.
但是如果被final同时修饰,在这里就会被直接赋值为3。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
使用该类所提供的功能,其中包括主动引用和被动引用
/***
* @author bincai
* @email [email protected]
*/
class InitClass{
static {
System.out.println("初始化InitClass");
}
public static String a = null;
public final static String b = "b";
public static void method(){}
}
class SubInitClass extends InitClass{
static {
System.out.println("初始化SubInitClass");
}
}
public class testswitch {
public static void main(String[] args) throws Exception{
//String a = SubInitClass.a;// 引用父类的静态字段,只会引起父类初始化
//String b = SubInitClass.b;// 使用类的常量不会引起父类和子类初始化
SubInitClass[] sc = new SubInitClass[10];// 定义类数组不会引起类的初始化
}
}
在类使用完之后,如果满足下面的情况,类就会被卸载
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。
在编译过程中就已经知道这个方法到底是哪个类中的方法,静态绑定效率要高一些。
只有final,static(重写static方法使用多态,调用的仍是父类的static方法),private和构造方法是静态绑定
在运行时进行绑定,具体原理:
父类不是接口,当子类和父类加载到虚拟机时,方法区就有了类的信息,方法区中有方法表,如果子类重写父类的方法,那么在各自方法表里方法的偏移量是一样的,当我们调用时:
如果父类是接口的话,无法根据偏移量来确定,则采用搜索方法表的形式,效率要低些。
// 默认构造函数。
HashMap()
// 指定“容量大小”的构造函数
HashMap(int capacity)
// 指定“容量大小”和“加载因子(可以大于1)”的构造函数
HashMap(int capacity, float loadFactor)
// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)
HashMap会对null值key进行特殊处理,总是放到table[0]位置
put过程是先计算hash然后通过hash与table.length取模计算index值,然后将key放到table[index]位置,当table[index]已存在其它元素时,会在table[index]位置形成一个链表,将新添加的元素放在table[index],原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,当元素数量达到临界值(capactiyfactor)时,则进行扩容,是table数组长度变为table.length2
在JDK1.8版本中,链表长度超过8改成红黑树
同样当key为null时会进行特殊处理,在table[0]的链表上查找key为null的元素
get的过程是先计算hash然后通过hash与table.length取摸计算index值,然后从table[index]上的链表或红黑树找到key,然后返回
public class TestTry {
public static void main(String[] args) {
WeakHashMap wak = new WeakHashMap();
//两个key都是匿名字符串对象(没有其他引用)
wak.put(new String("数学"), new String("优良"));
wak.put(new String("语文"), new String("良好"));
wak.put(new String("语文"), new String("良"));
//该key是一个系统缓存的字符串对象
wak.put("java", new String("好"));
System.out.println(wak);
//{java=良好, 数学=优良, 语文=良好}
//通知系统进行垃圾回收
System.gc();
System.runFinalization();
System.out.println(wak);//{java=好}
}
输出:
{java=好, 数学=优良, 语文=良}
{java=好}
在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等;相对于普通HashMap而言,只要key1和key2通过equals()方法返回true,且它们的hashCode值相等即可。
package test;
import java.util.*;
public class test1 {
public static void main(String args[]) {
IdentityHashMap idenmap = new IdentityHashMap();
idenmap.put(new String("语文"), 80);
idenmap.put(new String("语文"), 89);
idenmap.put("java", 80);
idenmap.put("java", 81);
idenmap.put(null,123);
idenmap.put(null,234);
idenmap.put("test",null);
System.out.println(idenmap);
}
}
输出:
{test=null, 语文=80, java=81, 语文=89, null=234}
EnumMap不允许使用null作为key,但允许使用null作为value。如果试图使用null作为key时将抛出NullpointerException。创建EnumMap是必须指定一个枚举类
如果只是查询是否包含值为null的key,或只是删除值为null的key,都不会抛出异常。
public class test1 {
public static void main(String args[]) {
EnumMap map = new EnumMap(Season.class);
map.put(Season.SUMMER, "夏天");
map.put(Season.SPRING, "春天");
System.out.println(map);
}
}
enum Season{
SPRING,SUMMER,FAIL,WINTER
}
ArrayList是基于数组的,是连续存放元素的,查找的时候直接根据数组下标就可以了。插入删除的时候,就要把修改的那个节点之后的所有数据都要移动,所以就慢了,插入删除速度和删除的元素离数组末端有多远有关,越远的话越慢。
长度默认是0,第一次往里面添加元素时底层数组会初始化一个长度为10的数组
扩容的话扩容为原来的1.5倍。具体步骤
在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
基于双向链表,可以被当作堆栈、队列或双端队列进行操作,大量的插入删除比较快,但是查询慢。
JDK1.5引入了枚举类型。
public enum StatusEnum {
SUCCESS(0),
//... 想象这里有几十种状态,不按ordinal来的
FAILED(100);
private int code; //这里仅居int的例子,还有转String的,同理
private StatusEnum(int status) {
this.code =status;
}
public int getCode() {
return code;
}
// 下面这部分是解决的办法
private static final Map<Integer, StatusEnum> code2enumMap =
new HashMap<Integer, StatusEnum>();
static {
for(StatusEnum st: StatusEnum.values()) {
code2enumMap.put(st.getCode(), st);
}
}
/**
* 按code从映射中找到枚举
* @param statusCode
* @return
*/
public static StatusEnum getByCode(int statusCode) {
return code2enumMap.get(statusCode);
}
}
通过类的全类名就能把这个类的所有方法和变量的信息(方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)找出来。如果明确知道这个类里的某个方法名+参数个数 类型,还能通过传递参数来运行那个类里的那个方法,这就是反射。反射的具体方法不多说。这里只说三种获得类类型的方式:
定义在一个方法或者作用域内,访问仅限于该方法或作用域内
使用一个匿名内部类我们必须继承一个父类或实现一个接口,它也没有class关键字
不能是抽象的,也不能被重复使用~
不能定义构造函数,不能存在静态成员变量和静态方法
只在编译阶段有效,运行时擦除
public class GenericTypes {
public static void method(List<String> list) {
System.out.println("invoke method(List list)" );
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List list)" );
}
}
public class StaticTest{
public static void main(String[] args){
GT<Integer> gti = new GT<Integer>();
gti.var=1;
GT<String> gts = new GT<String>();
gts.var=2;
System.out.println(gti.var);
}
}
class GT<T>{
public static int var=0;
public void nothing(T x){}
}
定义方式:
public @interface TestAnnotation {
}
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型,可以给成员变量赋默认值,也可以使用时候进行赋值
public @interface TestAnnotation {
int id();
String msg();
}
@Repeatable(Authorities.class)
public @interface Authority {
String role();
}
public @interface Authorities {
Authority[] value(); // 容器注解,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组
}
public class RepeatAnnotationUseOldVersion {
@Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
public void doSomeThing(){
}
}