目录
一:JVM,JDK,JRE的关系
二:面向对象的三大特性
三:多态知识点
四:重载(Overload)和重写(Override)的区别
五:==和equals
六:关于浮点数等算术问题
七:什么是跨平台性,原理是什么
八:访问修饰符
九:final的作用
十:final finally finalize区别
十一:this关键字用法:
十二:super关键字的用法
十三:this与super的区别
十四:static存在的主要意义
十四(1)静态类可以修饰哪些?修饰类吗(美团面试)
十四(2)什么时候会使用静态内部类(美团面试)
十五:break ,continue ,return 的区别及作用
十六:面向对象的五大基本原则
十七:抽象类与接口的对比
十八:普通类和抽象类有哪些区别?
十九:抽象类可以被final修饰吗
二十:成员变量与局部变量的区别有哪些
二十一:在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
二十二:一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么?
二十三:构造方法有哪些特性?
二十四:静态变量和实例变量区别
二十五:HashCode()和equals()
二十六:什么是内部类
二十七:JAVA只有值传递***********
二十八:JDK1.7后的字符串常量池
二十九:Java中IO流分哪几种
三十:Files常用方法
三十一:了解BIO,NIO,AIO吗
三十二:何为反射,获取class的三种方法
三十三:进程和线程
进程
线程
三十四:throw和throws的区别
三十五:基本数据类型的类型转换规则
三十六:装箱与拆箱
为什么需要包装类
拆箱与装箱
三十七:get请求和Post请求的区别
三十九:异常的分类
四十:HashTable和HashMap的区别
四十一:泛型与泛型擦除
(1)JVM(Java Virtual Machine)是JAVA虚拟机,JAVA程序需要在虚拟机上运行,不同的平台有自己的虚拟机,因此JAVA实现跨平台
(2)JDE(Java Runtime Environment)是JAVA运行环境,包含JVM和JAVA核心类库(如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等),想要运行好JAVA程序,只需要一个JRE即可
(3)JDK(Java Development Kit)是提供给开发人员使用的,包含了JRE与JAVA开发工具,如编译工具(javac.exe),打包工具(jar.exe)等
(1)继承
它可以使用现有类的功能,并在无需编写原来类的基础上对这些功能进行扩展
他有以下三大特点
1.子类拥有父类的所有方法和属性,但无法访问私有属性和方法
2.子类可以对父类进行扩展
3.子类可以用自己的方法实现父类的方法
(2)封装
把类的属性私有化,并提供一些外界可以访问的方法
(3)多态
一个对象具备多种形态,也可以理解为事务存在的多种体现形态(父类的引用类型变量指向了子类对象,或者是接口的引用类型变量指向了接口实现类的对象)
实现多态的方法
(1)接口实现
(2)继承父类方法重写
(3)同一个类中方法重载
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
1)对于==,比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象。
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
(1)float f=3.4;是否正确
(3)short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗
所谓的跨平台性,就是JAVA语言编写的程序,一次编译过后,可以在多个系统平台上运行
实现原理:JAVA程序是通过JAVA虚拟机在系统平台上运行的,只要该系统在相应平台上安装JAVA虚拟机,该系统就可以运行JAVA程序。
private:只在本类可见,可以修饰成员变量,方法。注意,不能修饰外部类
public:对所有类可见。使用对象:类,接口,方法,成员变量
default:总所周知在使用接口的时候,很多人都会遇到一个很尴尬的事情,在实现某个接口的时候,需要实现该接口所有的方法。这个时候default关键字就派上用场了。通过default关键字定义的方法,集成该接口的方法不需要去实现该方法。
在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected:对同一包内的类和所有子类可见,使用对象:方法,成员变量,不能修饰外部类
用于修饰类,属性和方法
(1)被final修饰的类不可被继承(太监类)
(2)被final修饰的类不可被重写
(3)被final修饰的变量不可变
如果是基本类型 ,不可改变指的是变量当中的数据不可改变
但是对于 引用类型 来说,不可改变的指的是变量当中的地址值不可改变
(1)finally可以修饰类,方法和变量,修饰类表示这个类不可被继承,修饰方法表示这个方法不能被重写,修饰变量表示该标量是一个常量,不可被改变。
(2)finally一般在try--catch代码块中,在处理异常时,通过我们将一定要执行的代码块放在finally代码块中,表示无论是否出现异常,该代码块都会执行,一般用来关闭资源。
(3)finalize是一个方法,属于Oject类的一个方法,因此所有的类都继承了它,该方法一般由垃圾回收器来调用,这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,由垃圾回收器调用finalize()。
this是自身的一个对象,代表对象本身,可以理解为:指向对象的一个指针。
this的用法在JAVA中可分为3种:
(1)普通的直接引用,this相当于是指向当前对象的本身
(2)形参与成员名字重名,用this区分
(3)引用本类的构造函数
super可以理解为指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类
super的三种用法
(1)普通的直接引用
与this类似,super相当于指向当前对象的父类的引用,这样就可以用super.xxx来引用父类成员变量
(2)子类的成员变量或方法与父类的成员变量或方法同名时,用super来区别
(3)引用父类的构造函数
super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
1.属性的区别:
this访问本类中的属性,如果本类没有此属性则从父类中继续查找。super访问父类中的属性。
2.方法的区别:
this访问本类中的方法,如果本类没有此方法则从父类中继续查找。super访问父类中的方法。
3.构造的区别:
this调用本类构造,必须放在构造方法的首行。super调用父类构造,必须放在子类构造方法首行。
4.其他区别:
this表示当前对象。super不能表示当前对象
PS:
super()和this()均需放在构造方法内第一行。
尽管可以用this调用一个构造器,但却不能调用两个。
this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
this表示这个类的当前实例,super表示父类的当前实例,static是属于类的,this是类的一个对象,当然调用了不了他,static太牛了,只有类名可以调用它,static叫静态方法,也叫类方法,就是在程序启动的时候,就会为这个方法分配一块内存空间,所以什么时候都可以调用这个方法。所以,静态方法里不能调用非静态方法,除非你先实例化那个类。如果在static修饰的方法中使用this关键字,而这个关键字就无法指向合适的对象;所以我们也说,静态成员不能直接访问非静态成员;
static注意事项
1、静态只能访问静态。2、非静态既可以访问非静态的,也可以访问静态的。
(1)用来形成静态代码块以优化程序性能
static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
(2)static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!
static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。
静态内部类的主要特点:
1 不持有外部类的引用(普通内部类持有);
2 可以直接创建实例,不需要先创建外部类(普通内部类需要);
3 可以有静态成员变量、方法(普通内部类不行)和非静态成员变量、方法;
4 只可以直接访问外部类静态成员,不可以直接访问外部类的非静态成员(普通内部类可以),需要通过传入外部类引用的方式才能访问。
使用场景:外部类与内部类有很强的联系,需要通过内部类的方式维持嵌套的可读性。内部类可以单独创建。内部类不依赖于外部类,外部类需要使用内部类,而内部类不需使用外部类(或者不合适持有外部类的强引用)。
break 跳出总上一层循环,不再执行循环(结束当前的循环体)
continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
(1)单一职能原则:类的功能要单一,不能森罗万象
(2)开放封闭原则:一个模块对扩展是开放的,对修改是封闭的
(3)里式替换原则:子类可以代替父类出现在父类能够出现的任何地方
(4)依赖倒置原则:高层次的模块不应该依赖低层次的模块,应该依赖于抽象,抽象不应该依赖于具体实现,具体实现应该依赖于抽象
(5)接口分离原则:设计时采用多个于特定用户有关的接口比采用一个通用的接口要好
抽象类不一定含有抽象方法,有抽象方法的一定是抽象类
接口只能有抽象方法
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。(接口也不能)
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域
成员变量:方法外部,类内部的变量
局部变量:方法里面的变量
区别
(1)作用域:
成员变量:针对整个类有效
局部变量:只在某个范围有效(一般指方法)
(2)存储位置:
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中
局部变量:在方法被调用,或者语句执行时存在,存储在栈内存中。当方法调用完或者语句结束后,就会自动释放
(3)生命周期:
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:当方法调用完,或者语句结束后,就自动释放。
(4)初始值
成员变量:有默认初始值
局部变量:没有默认初始值,必须使用前赋值
使用原则
在使用变量时需要遵循的原则为:就近原则,首先在局部范围找,有就使用;接着在成员位置找。
帮助子类做初始化工作。
主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
(1)名字与类名相同
(2)无返回值
(3)生成类时自动执行,无需调用
静态变量不属于任何对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只会为静态变量分配一次内存
实例变量:每次创建对象,都会为每个对象分配成员变量内存空间,实例 变量是属于实例对象的,在内存中,创建几次对象,就会有几份实例变量
hashCode和equals方法的关系
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()与equals()的相关规定
如果两个对象相等,则hashcode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashcode值,它们也不一定是相等的
为什么重写equals时必须重写hashCode方法?
重写equals方法后,判断对象相等,但其hashcode却不一致
-----------------------------------------------------------------------------------------------------------------
下面两个字符串:
String s1="Ma";
String s2="NB";
System.out.println(s1.hashCode()==s2.hashCode()); //true
System.out.println(s1.equals(s2)); //false
虽然两个Ma和NB两个字符串不同,但是他们有相同的hashcode值2484。
在java中,可以将一个类的定义放在另一个类的定义的内部,这就是内部类。内部类本身就是类的一个属性,与其他属性的定义方式一致
内部类的分类:成员内部类,静态内部类,局部内部类和匿名内部类
静态内部类:定义在类内部的静态类,就是静态内部类
静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类()
,如下:
成员内部类:定义在类内部,成员位置上的非静态类,就是成员内部类。
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类()
,如下:
局部内部类:定义在方法中的内部类
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类()
,如下:
匿名内部类:匿名内部类就是没有名字的内部类,日常开发中使用的比较多。
假如一个局部内部类只被用一次(只用它构建一个对象),就可以不用对其命名了,这种没有名字的类被称为匿名内部类
除了没有名字,匿名内部类还有以下特点:
匿名内部类必须继承一个抽象类或者实现一个接口。
匿名内部类不能定义任何静态成员和静态方法。
当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
我们为什么要使用内部类呢?因为它有以下优点:
一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
内部类不为同一包的其他类所见,具有很好的封装性;
内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
匿名内部类可以很方便的定义回调。
内部类有哪些应用场景
一些多算法场合
解决一些非面向对象的语句块。
适当使用内部类,使得代码更加灵活和富有扩展性。
当某个类除了它的外部类,不再被其他的类使用时。
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递
是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的
下面通过 3 个例子来给大家说明
example 1
在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.
array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。因此,外部对引用对象的改变会反映到所对应的对象上。
通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
example3
//能否实现这个 swap 方法
//让yesA=b,yesB=a?
String yesA = "a";
String yesB = "b";
//能否实现这个 swap 方法
//让yesA=b,yesB=a?
swap(yesA, yesB);
void swap(String yesA, String yesB){
String temp = yesA;
yesA = yesB;
yesB = temp;
}
我们需要明确答案:实现不了这个方法。
首先,我们要知道 String yesA = "a";
这行代码返回的 yesA 代表的是一个引用,这个引用指向堆里面的对象 a。
也就是说变量 yesA 存储的只是一个引用,通过它能找到 a 这个对象,所以表现出来好像 yesA 就是 a,实际你可以理解 yesA 存储是一个“地址”,Java 通过这个地址就找到对象 a。
因此,我们知道了, yesA 存储的值不是 a,是引用(同理,yesB也一样)。
然后,我们都听过 Java 中只有值传递,也就是调用方法的时候 Java 会把变量 yesA 的值传递到方法上定义的 yesA(同理 yesB 也是一样),只是值传递。
根据上面我们已经知道 yesA 存储的是引用,所以我们得知,swap方法
里面的 yesA 和 yesB 拿到的是引用。
然后调用了 swap 方法,调换了 yesA 和 yesB 的值(也就是它的引用)
请问,swap 里的跟我外面的 yesA 和 yesB 有关系吗?显然,没有关系。
因此最终外面的 yesA 指向的还是 a,yesB 指向的还是 b。
现在,我们明确了,Java 只有值传递。
例如执行了 String yesA = "a"
这行代码,我们现在知道 yesA 是一个引用指向了堆中的对象 a,再具体点其实指向的是堆里面的字符串常量池里的对象 a。
如果字符串常量池已经有了 a,那么直接返回其引用,如果没有 a,则会创建 a 对象,然后返回其引用。
这种叫以字面量的形式创建字符串。
还有一种是直接 new String
,例如:
String yesA = new String("a")
这种方式又不太一样,首先这里出现了字面量 "a",所以会判断字符串常量池里面是否有 a,如果没有 a 则创建一个 a,然后会在堆内存里面创建一个对象 a,返回堆内存对象 a 的引用,也就是说返回的不是字符串常量池里面的 a
我们从下面的实验就能验证上面的说法,用字面量创建返回的引用都是一样的,new String
则不一样
至此,你应该已经清晰字面量创建字符串和new String
创建字符串的区别了。
讲到这,经常还会伴随一个面试题,也就是
intern
以下代码你觉得输出的值各是啥呢?你可以先思考一下
String yesA = "aaabbb";
String yesB = new String("aaa") + new String("bbb");
String yesC = yesB.intern();
System.out.println(yesA == yesB);
System.out.println(yesA == yesC);
第一个输出是 false 应该没什么疑义,一个是字符串常量的引用,一个是堆内的(实际上还是有门道的,看下面)。
第二个输出是 true 主要是因为这个 intern 方法。
intern 方法的作用是,判断下 yesB 引用指向的值在字符串常量里面是否有,如果没有就在字符串常量池里面新建一个 aaabbb 对象,返回其引用,如果有则直接返回引用。
在我们的例子里,首先通过字面量定义了 yesA ,因此当定义 yesC 的时候,字符串常量池里面已经有 aaabbb 对象(用equals()方法确定是否有对象),所以直接返回常量池里面的引用,因此 yesA == yesC
我们把上面代码的顺序换一下:
String yesB = new String("aaa") + new String("bbb");
String yesC = yesB.intern();
String yesA = "aaabbb"; // 这里换了
System.out.println(yesA == yesB);
System.out.println(yesA == yesC);
把 yesA 的定义放到 yesC 之后,结果就变了:
实际上,我最初画字符串常量池的时候,就将其画在堆内,也一直说字符串常量池在堆内,这是因为我是站在 JDK 1.8 的角度来说事儿的。
在 JDK 1.6 的时候字符串常量池是放在永久代的,而 JDK 1.7 及之后就移到了堆中。
这区域的改变就导致了 intern 的返回值有变化了。
在这个认知前提下,我们再来看修改顺序后的代码具体是如何执行的:
String yesB = new String("aaa") + new String("bbb");
此时,堆内会新建一个 aaabbb 对象(对于 aaa 和 bbb 的对象讨论忽略),字符串常量池里不会创建,因为并没有出现 aaabbb 这个字面量。
String yesC = yesB.intern();
此时,会在字符串常量池内部创建 aaabbb 对象?
关键点来了。
在 JDK 1.6 时,字符串常量池是放置在永久代的,所以必须新建一个对象放在常量池中。
但 JDK 1.7 之后字符串常量池是放在堆内的,而堆里已经有了刚才 new 过的 aaabbb 对象,所以没必要浪费资源,不用再存储一份对象,直接存储堆中的引用即可,所以 yesC 这个常量存储的引用和 yesB 一样。
String yesA = "aaabbb";
同理,在 1.7 中 yesA 得到的引用与 yesC 和 yesB 一致,都指向堆内的 aaabbb 对象。
现在我们知晓了,在 1.7 之后,如果堆内已经存在某个字符串对象的话,再调用 intern 此时不会在字符串常量池内新建对象,而是直接保存这个引用然后返回。
你看这面试题坑不坑,你还得站在不同的 JDK 版本来回答,不然就是错的,但是面试官并不会提醒你版本的情况。
其实很多面试题都是这样的,看似抛给你一个问题,你好像能直接回答,如果你直接回答,那就错了,你需要先声明一个前提,然后再回答,这样才正确。
按照流的流向分,分为输入流和输出流
按照流操作单元划分,分为字节流和字符流
按照流的角色划分,分为节点流和处理流
Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
files.exits()查看文件是否存在
files.createFile()创建文件
files.createDictory()创建文件夹
files.delete()删除文件
files.copy()复制文件
files.move()移动文件
files.size()文件大小
files.read()读取文件
files.write()写入文件
BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO (New I/O): NIO是一种同步非阻塞的I/O模型,非阻塞模式正好BIO之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
IO流是阻塞的,NIO流是不阻塞的。
Java NIO使我们可以进行非阻塞IO操作。比如说,单线程中从通道读取数据到buffer,同时可以继续做别的事情,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的。另外,非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
AIO (Asynchronous I/O): AIO 也就是 NIO 2。在它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
对于任何一个类,都能知道这个类的属性和方法,对于任何一个对象,都能调用它的任何一个方法和属性,这种动态获取信息或者动态调用对象的方法的功能称为Java的反射机制。
获取反射的三种方法:
(1)Class clazz=class.forName("包名.类名")
(2)Class clazz=类名.class;
(3)Class clazz=对象.getClass();
一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
(1)throws跟在方法声明后面,后面跟的是异常类名
throw跟在方法体内,后面跟的是异常类对象名
(2)throws后面可以跟多个异常类名,用逗号隔开
throw只能抛出一个异常对象名
(3)throws表示抛出异常,由方法调度者来处理
throw表示抛出异常,由该方法体内的语句来处理
(4)throws表示有出现异常的可能性,并不一定出现这些异常
throw则是抛出了异常,执行throw一定出现了某种异常
自动转换规则:容量小的数据类型可以自动转换成容量大的数据类型
容量从小到大排序为:byte
强制转换规则:
(1)赋值运算符“=”右边的转换,若左边的级别>右边的级别,会自动转换;若左边的级别=右边的级别,不需要转换,若左边的级别<右边的级别,需要强制转换
(2)可以将整型常量直接赋值给byte,shor,char等类型变量,而不需要进行强制类型转换,前提是不超出其表述范围,否则必须进行强制转换
(3)所有的浮点运算都是以双精度进行的,即使两个float单精度量运算的表达式,也要先转换成double型,再做运算
(4)char型和short型参与运算时,必须先转换成int型
如输出'a'+1
很多人会有疑问,既然Java中为了提高效率,提供了八种基本数据类型,为什么还要提供包装类呢?
这个问题,其实前面已经有了答案,因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
自动装箱: 就是将基本数据类型自动转换成对应的包装类。
自动拆箱:就是将包装类自动转换成对应的基本数据类型。
Integer i =10; //自动装箱
int b= i; //自动拆箱
public static void main(String[]args){
Integer integer=1; //装箱
int i=integer; //拆箱
}
对以上代码进行反编译后可以得到以下代码:
public static void main(String[]args){
Integer integer=Integer.valueOf(1);
int i=integer.intValue();
}
int的自动装箱都是通过Integer.valueOf()方法来实现的,Integer的自动拆箱都是通过integer.intValue来实现的。
首先我们先了解两个概念
安全性:就是请求就只是为了获得数据而不会修改数据
.
幂等性:则指的是无论调用这个URL 多少次,都不会有不同的结果的 HTTP 方法。而在实际过程中,这个规定没有那么严格。例如在一个新闻应用中,新闻站点的头版不断更新,虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。
区别:
(1)get请求会将请求的数据拼接到URL后面,而post请求是将请求数据放到请求数据中
(2)get用于信息获取,符合幂等性和安全性;post请求不符合,因为他有可能改变服务器资源
(3)get请求会主动被浏览器缓存,如果下一次传输的数据相同,那么就会返回缓存中的内容;
而post请求不会
(4)GET 方法的 URL 一般都具有长度限制,而post的请求信息是放在数据中,没有长度限制;
(5)get请求只产生一个TCP数据包,浏览器会把请求头和请求数据一并发送出去
而post请求产生两个 TCP 数据包,浏览器会先将请求头发送给服务器,待服务器响应100
continue,浏览器再发送请求数据,服务器响应200 ok(返回数据)。
(1)cookie只能存储在浏览器或者本地,session存储在服务器
(2)cookie只能存储字符串类型,session存储任意的java对象
(3)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session) session更具有安全性v
(4)单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie,Session是没有大小限制和服务器的内存大小有关。
(1)运行时异常 虚拟机帮助我们捕获
(2)编译时异常 需要我们自己捕获
(3)error 是系统异常 只能重启解决
编译时异常:就是把.java文件转化成字节码.class文件的时候出现的异常
IOException 输入输出流异常
FileNotFoundException 文件找不到的异常
ClassNotFoundException 类找不到异常
DataFormatException 数据格式化异常
NoSuchFieldException 没有匹配的属性异常
NoSuchMethodException 没有匹配的方法异常
SQLException 数据库操作异常
TimeoutException 执行超时异常
运行时异常有哪些:
1.NullPointerExceptio 空指针异常
2.ClassNotFoundException 类不存在异常
3.IndexOutOfBoundsException 索引越界异常
4.NoSuchMethodError 方法不存在错误
5.NumberFormatException 数字格式转换异常
6.SQLException SQL语言异常
7.IOException 输入输出异常
8.IllegalArgumentException 方法参数错误
9.IllegalAccessException 无权访问异常
(1)HashTable是线程安全的,他每个方法都加了synchornized放啊,HashMap不是线程安全的
(2)HashTable不允许键值为null,HashMap可以
(3)HashTab继承Dictionary类,HashMap继承了map接口
(4) HashTable底层是散列表,HashMap底层是数组+链表
泛型(Generic),是一种参数化数据类型,它允许我们在编写程序代码的时候不用具体指定需要什么数据类型,而是等到具体使用的时候,将数据类型以参数的形式传递给程序,这就是泛型程序设计。
优点
缺点
泛型使用在一个类上面,这个类就称为:泛型类。
如果一个类,继承了泛型类,那么子类有两种处理泛型的方式:
- 第一种方式:子类给父类中的泛型参数指定具体的数据类型。
- 第二种方式:子类也定义为泛型类。
和泛型类基本类似,只不过是定义在接口上面。
泛型接口:泛型声明在接口上面,就叫做泛型接口。
泛型接口的实现
和泛型类的继承是类似的,也有两种方式:
- 第一种方式:实现类指定具体的数据类型。
- 第二种方式:实现类定义为泛型类(注意:实现类的泛型参数标识符必须和接口的泛型参数标识符一致,否则编译报错)。
泛型方法是在方法调用的时候,将泛型的数据类型作为参数传递给方法,而不是通过泛型类进行参数传递。
换句话说,泛型方法不需要依赖泛型类,它可以单独在一个普通的类中使用泛型参数。
除了用
可能有同学会想,已经有了
class Base{}
class Sub extends Base{}
Sub sub = new Sub();
Base base = sub;
上面代码显示,Base 是 Sub 的父类,它们之间是继承关系,所以 Sub 的实例可以给一个 Base 引用赋值,那么
List lsub = new ArrayList<>();
List lbase = lsub;
最后一行代码成立吗?编译会通过吗?
答案是否定的。
编译器不会让它通过的。Sub 是 Base 的子类,不代表 List和 List
但是,在现实编码中,确实有这样的需求,希望泛型能够处理某一范围内的数据类型,比如某个类和它的子类,对此 Java 引入了通配符这个概念。
所以,通配符的出现是为了指定泛型中的类型范围。
通配符有 3 种形式。
>被称作无限定的通配符。
extends T>被称作有上限的通配符。
super T>被称作有下限的通配符。
无限定通配符 >
无限定通配符经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关。
public void testWildCards(Collection> collection){
}
上面的代码中,方法内的参数是被无限定通配符修饰的 Collection 对象,它隐略地表达了一个意图或者可以说是限定,那就是 testWidlCards() 这个方法内部无需关注 Collection 中的真实类型,因为它是未知的。所以,你只能调用 Collection 中与类型无关的方法。
我们可以看到,当 >存在时,Collection 对象丧失了 add() 方法的功能,编译器不通过。
我们再看代码。
List> wildlist = new ArrayList();
wildlist.add(123);// 编译不通过
有人说,>提供了只读的功能,也就是它删减了增加具体类型元素的能力,只保留与具体类型无关的功能。它不管装载在这个容器内的元素是什么类型,它只关心元素的数量、容器是否为空?我想这种需求还是很常见的吧。
有同学可能会想,>既然作用这么渺小,那么为什么还要引用它呢?
个人认为,提高了代码的可读性,程序员看到这段代码时,就能够迅速对此建立极简洁的印象,能够快速推断源码作者的意图。
泛型的上界:
- 泛型的上界,是指规定传入的数据类型只能是某个具体数据类型及其子类型。
上界语法格式:
- T:表示泛型参数
- XXX:表示具体的数据类型,比如:String、Integer、等等。
泛型下界:
- 泛型的下界,是指规定传入的数据类型只能是某个具体数据类型及其父类型。
语法格式:
- XXX:是指具体的数据类型,比如:String、Integer、等等。
- T:是指泛型参数。
什么是泛型擦除???
泛型是只能在编译期间生效的,到了程序运行期间的时候,程序不认识那些泛型参数,所以在编译期间,泛型的那些参数都会被擦除,也就是被替换为具体的数据类型。
如果没有指定泛型的上下界,那么默认情况下,所有的泛型参数,在泛型擦除后将会是Object类型。
如果指定上界类型,那么泛型擦除之后,数据类型将和泛型上界的数据类型相同。
举个栗子:List extends String>,这个泛型擦除之后,数据类型将变成String类型。
如果指定下界类型,那么泛型擦除之后,数据类型将和泛型下界的数据类型相同。
举个栗子:List super Student>,这个泛型擦除之后,数据类型将变成Student类型。
8.泛型擦除的局限性
9.泛型数组
那如果要创建泛型数组呢???
有下面几种方式,创建泛型数组的方式: