自动类型转换:小容量的类型可以自动转换为大容量的类型,这里容量不是指的字节数,而是指的表述范围,比如说long(8个字节)->float(4个字节)
switch语句,从case开始一直到break表示结束
带标签的continue:
public class TestContinueWithLabels {
public static void main(String[] args) {
//输出100到205之间的质数
outer:for(int i=100; i<=205; i++){
for (int j=2; j <=i/2; j++){
if(i%j==0){
continue outer;
}
}
System.out.println( i+"");
}
}
}
无返回值就指定void,无类型
方法重载:实际上是完全不同完全独立的方法,只不过名称一样
方法里如果加入了static,就可以直接调用而不需要还得先new一个对象才能调用该方法
参数个数不同会造成重载,只有参数名不同不构成重载,只有返回类型不同不构成重载
递归结构中包括两方面:
递归头部分:什么时候不调用自身方法,如果没有头,将陷入死循环,也就是递归的结束条件;
递归体:什么时候需要调用自身方法。
递归比较耗资源。
面向过程与面向对象:
面向过程适合简单,不需要协作的事务。比如说开车,我只要列出1234步就好了,但如果说造车,我列出1234就力不从心了,自然而然的就想到对象object,
比如说车,有车窗、轮胎、发动机等等,面向对象适合大型的软件开发。
总结:
1.都是解决问题的思维方式,都是代码组织的方式。
2.解决简单问题可以使用面向过程。
3.解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。
对象与类:
对象object,或叫实例instance,带有属性和方法,比如说一个学生,有姓名年龄身高等属性,还有吃饭、睡觉、游戏等方法
类class,可以看作是一个模板,或者图纸,系统根据类的定义来造出对象。
构造方法,就是用于构建这个类的对象,无参的构造方法可以由系统自动创建。
java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区method area
栈:每个方法被调用的时候,就会创建一个栈帧。
1.栈描述的是方法执行(记忆重点)的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
2.jvm会为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
3.栈属于线程私有,不能实现线程间的共享!
4.栈的存储特性是“先进后出,后进先出”
5.栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆:存储对象,当new一个啥的时候就说明在堆里面存储了
1.堆用于存储创建好的对象和数组(数组也是对象)
2.jvm只有一个堆,被所有线程共享
3.堆是一个不连续的内存空间,分配灵活,速度慢!
方法区:(存储类相关信息)
1.jvm只有一个方法区,被所有线程共享!
2.方法区实际也是堆,只是用于存储类、常量相关的信息!
3.用来存放程序中永远是不变或唯一的内容。(类信息【class对象】、静态变量、字符串常量等)
构造方法:
构造方法的第一句总是super()
4.构造方法名称和类名必须保持一致。
要点:
1.通过new关键字调用!
2.构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return 返回某个值。(但可以使用return;用以结束)
3.如果我们没有定义构造器,则编译器会自动定义一个无参的构造函数,如果已定义则编译器不会自动添加!
垃圾回收机制(garbage collection):
饭店吃饭(把每一个对象都看做是一张桌子,然后有个服务员叫GC,看到哪个桌子脏了没客人去吃就去擦桌子),而之前C++的却没有GC服务员,
每张桌子需要顾客吃完了自己擦,如果没擦桌子就脏了,等到每张桌子都脏了之后该饭店也就关了。
所以GC的作用是:
1.发现无用的对象
2.回收无用对象占用的内存空间。
垃圾回收相关算法:
引用计数法
引用可达法(跟搜索算法)
通用的分代垃圾回收机制:不同的对象的生命周期是不一样的。为提高效率,将对象分为:年轻代、年老代、持久代
其中JVM将内存划分为Eden、Survivor(年轻代)和Tenured/Old(年老代)等,持久代放在方法区中。
这里不说持久代。
垃圾回收过程:
1.新创建的对象,绝大部分都会存储在Eden中;
2.当Eden满了(达到一定比例)不能创建新对象,则触发垃圾回收(GC),将无用对象清理掉,然后剩余对象复制到某个Survivor中,比如S1,同时清空Eden区
3.当Eden区再次满了,会将S1中的不能清空的对象存到另外一个Survivor中,如S2,同时将Eden中不能清空的对象也复制到S1中,保证Eden和S1均被清理;
4.重复多次(默认15次)Survivor中没有被清理的对象,则会复制到老年代Old(Tenured)区中。
5.当Old区满了,则会触发一个一次完整的垃圾回收(FullGC),之前新生代的垃圾回收称为(minorGC)
Minor GC:用于清理年轻代区域。Eden区域满了就会触发一次Minor GC,清理无用对象,将有用对象复制到Survivor1、Survivor2区中(这两个区,大小空间也相同,同一时间只有一个在用,一个为空)
Major GC:用于清理老年代区域
Full GC:用于清理年轻代、老年代区域,成本较高,会对系统性能产生影响。
_this的本质:
创建一个对象分为如下四步:
1.分配对象空间,并将对象成员变量初始化为0或空
2.执行属性值的显式初始化
3.执行构造方法
4.返回对象的地址给相关的变量
this的本质就是“创建好的对象的地址”!由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象”。
this最常用法:
1.在程序中产生二义性之处,应使用this来指明当前对象,普通方法中,this总是指向调用该方法的对象,构造方法中,this总是指向正要初始化的对象。
2.使用this关键字调用重载的构造方法,避免相同的初始化代码,但只能在构造方法中用,并且必须位于构造方法的第一句。
3.this不能用于static方法中。
static关键字:
在类中,用static声明的成员变量为静态成员变量,也称为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
static修饰的成员变量和方法,从属于类,不属于对象。普通变量和方法是从属于对象的。
静态初始化块:
构造方法用于对象的初始化块。静态初始化块,用于类的初始化操作!在静态初始化块中不能直接访问非static成员。
参数传值机制:
Java中,方法中所有参数都是“值传递”,也就是“传递的是值的副本”,也就是说,我们得到的是“原参数的复印件,而不是原件”,因此,复印件改变不会影响原件。
基本数据类型参数的传值:
传递的是值的副本,副本的改变不会影响原件。
引用类型参数的传值:
传递的是值的副本,但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了变化”。
public class User1 {
int id;
String name;
String pwd;
public User1(int id,String name){
this.id = id;
this.name = name;
}
public void testParameterTransfer01(User1 u){
u.name = "高小八";
}
public void testParameterTransfer02(User1 u){
u = new User1(123,"高三");
}
public static void main(String[] args) {
User1 u1 = new User1(100,"高小七");
u1.testParameterTransfer01(u1); //将u1传给u,u指向的是与u1同样的地址,那么改变u之后,u1也改变了
System.out.println(u1.name); //所以结果是高小八
u1.testParameterTransfer02(u1);
System.out.println(u1.name); //这里结果还是高小八,是因为16行new了个新对象,说明就换了个引用地址了,跟u1指向的不是同一个地址,所以u1还是不变
}
}
Java中类没有多继承,但接口可以多继承
子类重写父类的方法(override,跟方法重载overload不一样)
Object类是所有java类的根基类,也就意味着所有的java对象都拥有object类的属性和方法。
/**
* 重写toString方法
*/
public class TestObject {
public static void main(String[] args) {
// Object obj;
TestObject to = new TestObject();
System.out.println(to); //这里其实就是调用了to.toString方法
}
@Override
public String toString(){
return "重写的tostring方法!";
}
}
object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。
String str1 = new String("TXT");
String str2 = new String("TXT");
System.out.println(str1==str2);//结果为false,因为两者是不同的对象
System.out.println(str1.equals(str2);//结果为true,因为两者内容一样
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
super永远位于构造器的第一句,见下例:
public class TestSuper {
public static void main(String[] args) {
System.out.println("开始创建第一个childclass对象。。。。。。");
new ChildClass();
}
}
class FatherClass{
public FatherClass(){
// super();
System.out.println("创建father构造器!");
}
}
class ChildClass extends FatherClass{
public ChildClass(){
// super();
System.out.println("创建子构造器!!!");
}
}
这里输出结果依次为:
开始创建第一个childclass对象。。。。。。
创建father构造器!
创建子构造器!!!
为什么会这样,是因为构造器的第一句是super(),无论你有没有写,系统都会有这么一句的,所以上述情况其实是都有个看不见的super存在
在调用new ChildClass时,首先会先进行super(),也就到了fatherclass中了,而fatherclass也有super,所以就到了object中了,所以依次输
出结果是这样。child->father->object
封装:
封装的实现是通过访问控制符的,java的访问控制符有4个,private,default,protected,public,权限依次越来越大
同一个类 同一个包中 子类 所有类
private +
default + +
protected + + +
public + + + +
private表示私有,只有自己类能访问
defau表示没有修饰符修饰,只有同一个包的类才能访问
protected表示可以被同一个包的类以及其他包中的子类访问
public表示可以被该项目的所有包中的所有类访问
封装的使用细节:
一般写类都将其属性都设为private,外部类就访问不到,要想给其访问就对属性设置相应的set和get方法,比如说setName,getName等函数,
这也就是javabean,一个完整的javabean,要有set和get方法,还要有空的构造器。
多态:指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。
多态的要点:
1.多态是方法的多态,不是属性的多态(多态与属性无关)。
2.多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。(必须三个都有)
3.父类引用指向子类对象之后,用该父类引用调用子类重写的方法,此时多态就出现了。
public class TestPolym {
public static void main(String[] args) {
Animal a = new Animal();
AnimalCry(a);
Dog b = new Dog();
AnimalCry(b); //这里就是父类引用指向子类对象,(b是子类,但参数其实是父类对象)所以一共满足多态的三个必要条件,就产生了多态,如果没有多态,要想产生同样的效果就比较繁杂了,
// 就需要写很多次的AnimalCry,每个子类写一个
}
static void AnimalCry(Animal a){
a.shout();
}
// static void AnimalCry(Dog a){
// a.shout(); //像这样
// }
// static void AnimalCry(Dog a){
// a.shout();
// }
}
class Animal{
public void shout(){ //这里有方法重写也有继承
System.out.println("叫了一声!!!");
}
}
class Dog extends Animal{
public void shout(){
System.out.println("汪汪汪!!!");
}
}
class Cat extends Animal{
public void shout(){
System.out.println("喵喵喵喵!!!");
}
}
final关键字
1.修饰变量:被final修饰的变量不可改变,一旦赋了初值,就不能被重新赋值。
2.修饰方法:该方法不可被子类重写,但是可以被重载。
3.修饰类:修饰的类不能被继承。比如:Math、String等
数组初始化:
1.静态初始化:int a = {0,2,4};
2.默认初始化:int a = new int[3];
3.动态初始化:int a = new int[3];a[0]=1;
//自动装箱:编译器自动将数据类型转换为包装类 Integer a = 234;实际上编译器是Integer a = Integer.valuesOf(234)
//自动拆箱:编译器自动将包装类转换为数据类型 int b = a;实际上编译器是做了int b = a.intValue()操作。
//String是不可变的,因为源码中的char前有final修饰
//StringBuilder是可变的,因为char中没有用final修饰,同理StringBuffer也是一样
//StringBuilder(线程不安全,效率高)和StringBuffer(线程安全,效率低),一般常用StringBuilder,因为线程不安全很少考虑
StringBuilder有个方法:insert(index,插入字符)可以链式调用,观察源码:是因为其调用了return this,返回了其本身
1.Date时间类,2.时间格式化类DateFormat(把时间对象转换成字符串对象,把字符串对象转换成时间对象)
3.Calendar日期类 new GregorianCalendar(2999,10,9,22,10,50)
异常类层次结构图
throwable
1.Error(不需要我们管的,类似于开车发动机坏了司机怎么操作都没用,表示jvm已经处于不可恢复的崩溃状态中)
2.Exception
1.Checked Exception:已检查异常。编译器处理,一般像语法异常啊。。。
2.Runtime Exception:也叫unchecked exception,运行时异常,多数由程序员自身的逻辑错误产生。
try-catch-finally:这里的finally语句,无论前面是经过哪一步的,最后都会走finally语句块,只有在一种情况下例外,
如果在执行finally语句块之前遇到了System.exit(0)结束程序运行。
容器collection(interface):
数组就是一种容器,可以在其中放置对象或基本类型数据
1.set(interface没有顺序,不可重复,也只能放一个null):collection有的方法它都有,也没有新增的像list关于索引等方法
hashset是采用哈希算法实现,底层实际是用hashmap实现的(hashset本质上就是一个简化的hashmap),因此查询和增删效率都比较高。
2.List(interface有顺序,可重复)
1.ArrayList:底层是数组,可以进行扩容。查找效率高,增删效率低,线程不安全
2.LinkedList:底层是双向链表。增删效率高,查找效率低,线程不安全
3.Vector:底层是数组,相关方法都加了同步检查,“线程安全,效率低”。
一般的,只有要求是线程安全的时候,才会使用vector,其他情况都不怎么使用它,而是选择其他两者。
3.Map(interface):Key-Value形式,底层是数组加链表,结合两者特点,熟记Map的储存方式与查找方式(与key的hashcode值所产生的hash有关)
1.hashmap:线程不安全,效率高,允许key或value为null
2.hashtable:线程安全,效率低,不允许key或value为null
理解泛型:考虑一个一个的容器里,我要放入猪肉跟羊肉,如果放在同一个容器里,取出来的时候就得注意是否是想要的羊肉,
那么泛型就是,我把猪肉跟羊肉放入不同的容器里,并且还写有标签,那么我再想要羊肉的话就可以直接取不用再看了。
那么泛型不正规的说就是相当于在容器上添加一个标签,可以帮助我们建立类型安全的集合。
用专业化的话来说,泛型的本质就是“数据类型的参数化”。我们可以把泛型理解为数据类型的一个占位符(形式参数),即告诉
编译器,在调用泛型时必须传入实际类型。
public class TestGeneric {
public static void main(String[] args) {
MyCollection
mc.set("didi",0);
// mc.set(1111,1); //这里开始就不行了,因为泛型是String,而这里是整型
String a = mc.get(0);
// int b =(int)mc.get(1);
System.out.print(a);
}
}
class MyCollection
Object[] objs = new Object[5];
public void set(E e,int index){
objs[index] = e;
}
public E get(int index){
return (E)objs[index];
}
}
TreeMap:底层是红黑二叉树,在排序的时候用
Comparable接口:中有个compareTo方法,其中比较大小如果为负数表示小于,0表示相等,1表示大于,要想倒序,将正负1换下即可
TreeSet:底层实际是用TreeMap写的,内部维持了一个简化版的TreeMap,通过key来存储Set的元素,TreeSet内部需要对存储的元素进行
排序,因此,对应的类需要实现Comparable接口,这样根据compareTo()方法比较对象之间的大小,才能进行内部排序。
Iterator迭代器遍历容器元素(List/Set/Map),统一的遍历容器的方式:
public class TestIterator {
public static void main(String[] args) {
// listIterator();
// setIterator();
// mapIterator1();
mapIterator2();
}
public static void listIterator(){
List
list.add("aa");
list.add("bb");
list.add("cc");
for(Iterator
String temp = iter.next();
System.out.println(temp);
}
}
public static void setIterator(){
HashSet
set.add("aa1");
set.add("bb2");
set.add("cc3");
for(Iterator
String temp = iter.next();
System.out.println(temp);
}
}
//map的迭代器与list/set有所不同。要先根据map生成set,然后再iterator
//这是第一种方式:先由map生成entrySet,然后由entrySet生成iterator进行迭代
public static void mapIterator1(){
HashMap
map1.put(1,"aaa");
map1.put(2,"ccc");
map1.put(3,"ddd");
Set
for(Iterator
Map.Entry
System.out.println(temp.getKey()+"——————"+temp.getValue());
}
}
//第二种方式,就是先由map获得该map的keySet,然后再遍历该keySet,再获取相应的value值。
public static void mapIterator2(){
HashMap
map2.put(1,"aaa");
map2.put(2,"ccc");
map2.put(3,"ddd");
Set
for(Iterator
Integer temp = iter.next();
System.out.println(temp.toString()+map2.get(temp));
}
}
}
Collections工具类与collection接口不同,有很多方法,比如说:
Collections.sort(list); 排序
System.out.println(Collections.binarySearch(list,"dai:1")); 二分查找某个元素是否在list中
Collections.reverse(list); 逆序
Collections.shuffle(list); 洗牌
IO:
在整个java.io包中最重要的就是5个类和3个接口,掌握了这些IO的核心操作
5个类:
1.File类:数据源,文件类
2.InputStream:字节输入流(我们操作的数据有两种:字符跟字节)
3.OutputStream:字节输出流
4.Reader:字符输入流
5.Writer:字符输出流
3个接口:文件不是直接存在java中,而是存在硬盘中,那这个硬盘就需要操作系统去操作,因此java程序需要跟操作系统进行交流,
当java已经读完了这个文件,需要告诉操作系统可以释放你操作系统该文件的资源了,这里就涉及到流关闭接口了。
1.Closeable:关闭流接口
2.Flushable:刷新流接口
3.Serializable:序列化接口
当我们手上拿到一个对象的时候,如果放在内存中,当电脑一关,数据就没了,因此为了便于对对象的传输,定期
将其存储到存储文件中,这时候我们就把这种存储对象称作序列化。
流分类1:(按直接操作数据源还是间接操作数据源来分)
字节流:直接从数据源或目的地读写数据
处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能。
节点流和处理流的关系:
1.节点流处于io操作的第一线,所有操作都必须通过他们进行;
2.处理流可以对其他流进行处理(提高效率或操作灵活性)。
流分类2:按数据类型分为字节流跟字符流(底层还是基于字节流操作,自动搜索了指定码表)。