基本类型
Java随机数
1, double b = Math.random(); //返回值是[0.0,1.0)
生成 1~100 int I = (int) (Math.random()*100+1);
生成n~m之间的随机整数:int num = (int) (Math.random()*(m-n+1)+n);
2, Random类:
Random r1 = new Random(); // 不带种子的构造器;
Random r2 = new Random(System.currentTimeMillis()); //获取当前时间的毫秒数作为随机数种子。种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系。
int I = r1.nextInt(n); // [0,n)之间的随机数
int j = r2.nextInt(n); // 随机数种子一样时,生成的随机数序列顺序是一样的。
3, currentTimeMillis():
8大基本类型(内置数据类型):
boolean: 1bit,只有false和true。
byte:8bit,1字节,有符号的二进制补码表示 -128~127
short: 2字节,有符号的二进制补码表示
char: 2字节,unicode字符。
int:(默认整型) 4字节,有符号的二进制补码表示
float: 4字节,单精度
long: 8字节,有符号的二进制补码表示
double(默认浮点数):8字节,双精度
包装类型: 自动包箱、自动拆箱
Boolean,Byte, Short, Character, Integer, Float, Long, Double.
!!Integer包装型,整数字面量在-128~127之间时不会new新的Integer对象,而是直接引用常量池中的Integer对象。
其他都为引用类型:
对象
数组
String
枚举
常量
Final修饰,一次赋值,不可修改
String属于常量字符串
类型转换
转换从低到高:byte,short,char -> int -> long -> float -> double
- 自动类型转换:转换前的数据类型的位数低于转换后的。
- 强制类型转换:转换的数据类型必须是兼容的。
!!
short s1 = 1; s1=s1+1; // 错误,1默认为int型,需要强制转换。
Short s1 =1 ; s1+=1; //正确,隐含强制类型转换。
String
创建字符串
用字符串直接赋值
使用String的构造器来创建字符串
String str = “This is String”;
String str = new String(“this is String”);
String的不可变
String被final修饰,不可被继承;
没有提供任何会修改对象状态的方法;
所有域都是final,并且是private
Java中String类其实就是对字符数组的封装
Jdk1.6中,string的成员变量,都是private final的:
char数组value,
String在这个value数组中的起始位置offset,
String所占的字符的个数count,
String对象的哈希值的缓存hash。
Jdk1.7中,string的主要成员变量变为了,都是private final的:
char数组value(value中的所有字符都是属于String这个对象),
hash。
通过反射改变String
因为成员变量都是private final,所以一旦初始化不能被改变,但是value是一个引用变量,可以通过反射,获取value属性,改变访问权限,然后通过获得的value引用改变数组内容。
不可变类的好处:
- 实现了字符串池,运行时节约堆空间,不同的字符串变量都指向池中的同一个字符串。
- 字符串可变会引起严重的安全问题,比如用户名和密码都是字符串形式传入获得数据库的连接,或者socket编程中的主机名和端口。
- 字符串不可变可以实现多线程安全。
- 字符串创建的时候hashcode就被缓存了,不必重新计算,所以字符串很适合做map的键。
区分对象和对象的引用:
String s = “ABCabc”;
s=”123456”;
// s只是一个String对象的引用,不是对象本身,所以s的引用是可以变得。
String对象的存储/String的比较
字符串常量池:jvm为了字符串的复用,减少字符串对象的重复创建。
String str1 = “123”; // 直接在字符串常量池中查找是否存在123,若存在返回引用,若不存在,新建一个string对象存入字符串常量池中。
String str2 = new String(“123”); // new关键字,直接在堆上生成一个string对象。不理会常量池中是否有这个值。
str2 = str2.intern(); // String类提供了一个native方法intern()用来将这个对象加入字符串常量池。首先也是检查字符串常量池中是否存在这个对象,若存在直接返回引用,不存在加入str2并返回。
String类提供了一个native方法intern()用来将这个对象加入字符串常量池
常用方法:
length()
charAt()
toLowerCase()
toUpperCase()
toCharArray()
trim()
比较方法:
equals()
equalsIgnoreCase()
compareTo()
compareToIgnoreCase()
startsWith(String prefis)
startsWith(String prefis,int toffset)
endsWith(String suffix)
contains(String s)
(String), String.valueOf(), toString()方法:
将非String类型的对象,转换成String
(String):强制转换
toString():类.toString(),比如Integer.toString(), StringBuffer.toString()等等。
toString()是在Object中定义的,因此,任何继承Object的类都具有这个方法。
使用toString()的对象不能为null,否则会抛出异常java.lang.NullPointerException。
String.valueOf():
当参数不是类对象时,比如String.valueOf( int i), 字符数组等。
首先对转换的对象进行判空检测,如果为空,则返回“null”字符串,以至于不会报出空指针异常。解决了toString()使用对象不能为空的问题
String对象比较 “==”和”equals”的区别
用equals() 比较,不要用‘==’
1,对于==
如果作用于基本数据类型的变量,则直接比较其存储的“值”是否相等。
如果作用于引用类型的变量,则比较的是所指向的对象的地址。
2,对于equals方法,
equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象。
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
3,Equals对于String的底层源码:
首先,判断地址是否相等“==”,若相等,直接返回。
其次,在判断第二个类是否是String类型,不是直接返回false。
然后,判断字符串长度是否一样,若不一样则返回。
最后一次比较String中的每个字符,若有不一样的则返回。
String,StringBuilder 以及 StringBuffer
关于这三个字符串类的异同之处主要关注可变不可变、安全不安全两个方面:
1)StringBuffer(同步的)和String(不可变的)都是线程安全的,StringBuilder是线程不安全的;
2)String是不可变的,StringBuilder和StringBuffer是不可变的;
3)String的连接操作的底层是由StringBuilder实现的;
三者都是final的,不允许被继承;
StringBuilder 以及 StringBuffer 都是抽象类AbstractStringBuilder的子类,它们的接口是相同的。
Integer
Integer类的parseInt和valueOf的区别
Integer/Double/Float/.parseInt(String s, int radix)方法: 返回的是基本类型。
Integer/Double/Float/.valueOf(String s, int radix)方法: 返回的是包装类型, valueOf方法实际上是调用了parseInt方法。
Integer和Int的区别
区别:
- Integer是int的包装类,默认初始值是null,必须先实例化,自带很多方法可以用;int是基本数据类型,默认初始值为0,可以直接使用,能做一些基本的加减乘除赋值等操作。
- 存储方法和位置:基本类型是直接存储变量的值保存在堆栈中能高效的存取,封装类型需要通过引用指向实例,具体的实例保存在堆中。
- 二者交叉会出现自动装箱和拆箱操作。
- 集合类都是装object的,用int不可以,只能用Integer,泛型定义也不可以用基本类型。
什么时候自动装箱和拆箱
编译期进行的自动装箱和拆箱。
- 自动装箱: 如Integer I = 100;基本数据类型赋值给引用数据类型的时候;执行的是Integer I = Integer.valueOf(100);valueOf对于-128到127之间的数,会进行缓存。
- 自动拆箱:如,Integer a = new Integer(100); int m = a;基本数据类型和引用数据类型做运算的时候;执行的是int m = a.intValue();
比较
- Int和Integer(无论new否),都为true,(integer自动拆箱为int再去比较,实际为两个int变量的比较)
- 两个new出来的Integer,比较结果为false,(new生成的是两个对象,其内存地址不同);
- Integer和new出来的Integer,比较结果为false,(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
- 两个非new出来的Integer,如果数值在-128~127之间,则为true,否则为false。 (valueOf()函数会对-128到127之间的数进行缓存。范围之外的数实际是new得到的,地址不同)
涉及自动类型转换时的integer和int的比较:
- equals 运算符不会进行基本类型转换
- ==、+-*/可以进行基本类型转换
如:
Integer a = 1;
Integer b = 2;
Long g = 3L;
Long h = 2L;
System.out.println(g==(a+b)); // true;a+b:自动拆箱;== 自动类型转换(int转为long)
System.out.println(g.equals(a+b)); // false; a+b:自动拆箱; equals不进行自动类型转换。
System.out.println(g.equals(a+h)); // true;a+h:自动拆箱,自动类型转换为long。
Object
Equals
Object中equals和==是相同的,判断是否具有相同的引用。
String,Integer等类对equals进行了重写,判读对象所含内容是否一致。
Hashcode
对象导出的一个整数值,object中指对象的存储地址。
为什么重写equals就要重写hashcode
Hashcode三条约定:
- Equals方法中比较所用信息没有改变时,则同一对象多次调用hashcode返回相同结果。
- 想等对象具有相同的hashcode
- 不想等对象不要求必须产生不同的hashcode
如果重写equals不重写hashcode,违反了第二条约定。
toString
返回表示对象值的字符串
Clone()
参考:
根据其成员对象是否也克隆,对象克隆可以分为两种形式:深克隆 和 浅克隆 。
1)对象创建:new创建或者clone()复制
区别:虽然都是创建了一个新对象,但是clone创建的对象里引用类型的数据与原对象引用类型的对象使用的是一个引用地址。所以clone()方法又叫浅拷贝。
new: 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
Clone():首先分配内存,分配的内存和源对象(即调用clone方法的对象)大小相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部 。
!! clone复制的对象里面若有引用形实例数据是,新对象和原对象引用型实例数据是同一个引用!并没有新建。
2)如果想要实现深拷贝,实现Clonable接口,覆盖并实现clone方法。
除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的。
下面的例子:
克隆body的时候,实例域中有一个引用型数据head,如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。(新克隆的head和原来的head不是同一个内存了。)
浅拷贝:
深拷贝
3)彻底的深拷贝:如果在拷贝一个对象时,要想让这个拷贝的对象和源对象完全彼此独立,那么在引用链上的每一级对象都要被显式的拷贝。
上面的2)中实现的body拷贝还是不彻底的深拷贝,若head类中可能还有引用型数据face,则face没有实现深拷贝。
所以需要在head中也要重写clone()方法,将源对象引用的face对象也clone一份。。
创建彻底的深拷贝是非常麻烦的,尤其是在引用关系非常复杂的情况下, 或者在引用链的某一级上引用了一个第三方的对象, 而这个对象没有实现clone方法, 那么在它之后的所有引用的对象都是被共享的。
所以彻底深拷贝,几乎是不可能实现的。
类
面向对象的特征
1)继承:从已有类继承信息创建新类的过程。子类自动共享父类的非私有数据和方法的机制,是一种类之间的关系,提高了软件的可重用性和扩展性。
2)封装:是把数据和操作数据的方法绑定起来,对数据的访问只能通过已经定义的接口。封装的目的是实现高内聚低耦合,对象是封装的基本单位。
3)多态:允许不同子类型的对象对同一消息做出不同的相应。分为编译时多态(重载:方法名必须相同,参数个数和类型,返回类型可以不同)和运行时多态(重写)。运行时多态需要做:方法重写(子类继承父类并重写父类中已有的方法),对象的构造(用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同表现出不同的行为)。
4)抽象:将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象,抽象是关注对象有哪些属性和行为,而不关注行为的细节。
访问修饰符的区别
Public: 当前类、同一包中的类、子类、其他包
Protected: 当前类、同一包中的类、子类
Default: 当前类、同一包中的类
Private: 当前类
Final关键字
- 修饰实例域:一次初始化不能更改,修饰的是引用类型时,指引用的地址值不能修改。
- 修饰方法:不能被重写,类的private方法隐式指为final。
- 修饰类:不能被继承,如String,System。
Static
创建独立于对象的域变量或方法。
随着类的加载而加载,存放在静态常量池里。
- static方法:没有this,不能访问非静态方法和成员变量。
- static变量:所有对象同享,内存中只有一个副本。
- static常量:final修饰。
- static块:提升程序性能,类初次被加载时,按顺序执行静态块,且只执行一次。
- 静态内部类:
接口和抽象类的区别
用法:
- 接口只声明了方法,不实现,抽象类可以有实现
- 接口中不能有构造函数
- 接口中只能由静态常量,抽象类中成员标量类型任意。
- 可以实现多个接口
应用:
- 接口主要用于定义模块之间的通信契约,提供了一个准则。
- 而抽象类在代码实现方面发挥作用,可以实现代码的重用.
构造器
构造器不能被继承,因此不能被重写,但是可以被重载。
重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
重载和重写都是实现多态的方法,重载编译时多态,重写运行时多态。
不能只根据返回类型进行区分。
内部类
定义在类中的类
- 成员内部类:可以获得外部类的引用。
- 局部内部类:定义在一个方法或者一个作用域中的类。
- 匿名内部类:在实现父类或者接口中的方法中同时产生一个相应的对象。
- 静态内部类:不依赖于外部类,不能使用外部类的非静态方法或属性。
Java集合
Java集合框架包含两种类型的容器:collection接口和map接口
collection包含三种子接口set,list,queue
List
- 有序,可重复的集合
- 实现类:
Arraylist:底层实现数组,查询快,增删慢,线程不安全,效率高。
Vector:底层实现数组,查询快,增删慢,线程安全,效率低。
Linkedlist:底层实现双向链表,查询慢,增删快,线程不安全,效率高。
Set
- 无序、不可重复的集合
- 实现类:
HashSet:无序,不可重复,线程不安全,集合元素可以为null
LinkedHashSet:底层实现链表和哈希表,有序(顺序),不可重复,线程不安全。
TreeSet:底层使用红黑树算法,擅长于范围查询,元素有序(排序),不可重复,线程不安全。自然排序或者比较器排序实现元素排序;compareTo或compare返回值是否为0保证唯一性。
Map
- Key-value的键值对,key不允许重复,value可以
- 实现类:
hashMap:采用哈希表算法,key无序且不允许重复,key判断重复的标准是:key1和key2是否equals为true,并且hashCode相等
hashtable:安全
linkedHashMap:
TreeMap:采用红黑树算法,key有序且不允许重复,key判断重复的标准是:compareTo或compare返回值是否为0
Queue
- 实现类:
Linkedlist
PriorityQueue
Queue<Object> queuesample = new LinkedList<>();
方法:
Offer()
Poll()
Size()
isEmpty();
PriorityQueue
类 PriorityQueue
java.lang.Object
——继承者 java.util.AbstractCollection
———– 继承者 java.util.AbstractQueue
—————–继承者 java.util.PriorityQueue
- 默认为最小堆
- 不允许使用null或者不可比较对象
- 初始容量为11,自动扩容(先申请一个更大的数组,然后复制过去),无界
- 线程不安全
- 数组实现,并拥有优先级
- 插入元素:插入最后一个,并向上调节
删除:将最后一个元素放在堆顶,向下调整。
方法:
- add()和offer(): Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则则会返回false。对于PriorityQueue这两个方法其实没什么差别。
- element() / peek():获取但不删除队首元素,也就是队列中权值最小的那个元素,二者唯一的区别是当方法失败时前者抛出异常,后者返回null。
- remove()/ poll():获取并删除队首元素,区别是当方法失败时前者抛出异常,后者返回null。
PriorityQueue: JDK1.5开始提供的新的数据结构接口,默认内部是自然排序,结果为小顶堆,也可以自定义排序器,比如下面反转比较,完成大顶堆。
PriorityQueue 一个基于优先级的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。该队列不允许使用 null 元素也不允许插入不可比较的对象(没有实现Comparable接口的对象)。
PriorityQueue 队列的头指排序规则最小那哥元素。如果多个元素都是最小值则随机选一个。
PriorityQueue 是一个无界队列,但是初始的容量(实际是一个Object[]),随着不断向优先级队列添加元素,其容量会自动扩容,无需指定容量增加策略的细节。
//PriorityQueue默认是小顶堆,实现大顶堆,需要反转默认排序器
异常
异常
异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。
Throwable:error,exception(受检异常IOException、SQLException、ClassNotFoundException,非受检异常RuntimeException)
Exception可以被程序本身处理,Error无法处理
Error
是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
如OutOfMemoryError,Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的
Exception
- 非受检异常RuntimeException:可以选择捕获处理,也可以不处理。如空指针错误,数据下标越界,方法传递参数错误,字符串转数字错误,数据类型转换错误;算术条件异常
- 受检异常IOException、SQLException、ClassNotFoundException:必须进行处理的异常,不处理不能编译通过。文件已结束异常,文件未找到异常。
异常处理机制
- Throws:声明异常,不处理,交给上层。
- Throw:实例化异常对象,抛出异常。
- Try,catch,finally:捕获异常。
什么时候捕获异常,什么时候向上抛出异常?
应该考虑当前作用域是否有有能力处理这一异常,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理。
断言
调试方法,只用于开发测试阶段,保证正确性,异常保证健壮性
assertionError:不能捕获的异常。
Assert 条件:表达式 条件为false时,抛出异常
其他
泛型
- 参数化类型,提高重用率,安全性(编译时检查),可读性
- 泛型出现之前:object实现参数的任意性。缺点:强制转换,留到运行时检查。
- 泛型类:具有一个或多个类型变量的类(方法的返回类型,域,及局部变量)
泛型方法:放在返回类型之前,与使用了泛型的成员变量区别!
泛型接口:
- 类型变量的限定:上界 extends, 下界 super,无限通配符 <?>
- 泛型的实现:类型擦除。编译器在编译时擦除了所有泛型类型相关的信息,运行时不保存任何泛型类型相关的信息。编译为字节码时先进行类型检查,类型参数用限定类型的第一个或者object代替。
自动装箱和自动拆箱
基本数据类型与包装器类型的自动转化
如何实现对象克隆
- 实现cloneable接口并重写clone方法;
- 实现serializable接口,通过对象的序列化和反序列化实现克隆
Java反射机制
- Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。
- 这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射机制。
- Class 类与java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor类(每个类都实现了Member 接口)。这些类型的对象时由JVM 在运行时创建的,用以表示未知类里对应的成员。
- 在Java 中可以通过三种方法获取类的字节码(Class)对象:
Object 类中的getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象
所有数据类型都具备一个静态的属性.class 来获取对应的Class 对象。但是还是要明确到类,
通过Class.forName() 方法完成,必须要指定类的全限定名
Java反射中Class.forName()加载类和使用ClassLoader加载类的区别
在java中Class.forName()和ClassLoader都可以对类进行加载。
ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中;只是将class文件加载到jvm中,不执行static的内容;
Class.forName()方法实际上也是调用的CLassLoader来实现的;forName 会初始化Class,而 loadClass 不会。
代理
- 静态代理:代理对象和被代理对象实现相同的接口或者继承相同的父类
- 动态代理:运行时创建一组给定接口的新类。
- Cglib代理:运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。