一、基本语法
1、数据类型
JAVA基本数据类型
- int 默认值0
- short 默认值0
- long 默认值0L
- boolean
- char 默认值'\u000'
- float 默认值0F
- double 默认值0D
- byte 默认值0
JAVA引用数据类型
- 类lass
- 接口interface
- 数组[]
2、访问修饰符
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
- public : 对所有类可见。使用对象:类、接口、变量、方法
3、运算符
&和&&的区别
- &运算符有两种用法:1)按位与 2)逻辑与
- &&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。
4、关键字
1.final有什么用
- 被final修饰的类不可以被继承
- 被final修饰的类不可以被重写
- 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是指向的内容,引用指向的内容是可以改变
2.final、finally、finalize
- final可以修饰类、变量、方法、修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示改变量是一个常量不能被重新赋值
- finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法放入finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码
- finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System.gc()方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断
3.this关键字
-
this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针
-
this的用法在java中大体可以分为三种:
- 普通的直接引用,this相当于是指向当前对象本身
- 形参与成员名字重名,用this来区分
public Person(String name, int age) {
this.name = name;
this.age = age;
}
c.引用本类的构造函数
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
}
4.super关键字的用法
-
super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个类
-
super的三种用法
- 普通的直接引用
与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员
- 子类中的成员变量或方法与父类的成员变量或方法同名时,super进行区分 class Person{ protected String name;
public Person(String name) {
this.name = name;
}
}
class Student extends Person{
private String name;
public Student(String name, String name1) {
super(name);
this.name = name1;
}
public void getInfo(){
System.out.println(this.name); //Child
System.out.println(super.name); //Father
}}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("Father","Child");
s1.getInfo();
}
}
-
引用父类的构造函数
- super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)
- this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)
- super:它引用当前对象的直接父类中的成员(用来访问父类中被隐藏的父类中成员数据或函数,基本与派生类中由相同成员定义时如:super变量名super成员函数名(实参))
- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量)
- super()和this()类似,区别是super()在子类中调用父类的构造函数,this()在本类的其他构造方法
- super()和this()均需要放在构造函数内的第一行
- 尽管可以用this调用一个构造器,但却不能调用两个
- this和super不能同时出现在同一个构造函数里面,因为this必然会调用其它的构造函数,其他的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过
- this()和super()都指向的时对象,所以,均不可以在static环境中使用。包括static变量,static方法,static语句块
- 从本质上来讲,this时一个指向本对象的指针,然而super是一个java关键字
5.static存在的主要意义
- static的主要意义是在于创建一个独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法!
- static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次
- 为什么说static块可以用来优化程序性能,是因为他的特性只会来类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行
6.static的独特之处
- 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享
- 在该类被第一次加载的时候,就会去加载static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值
- static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值
- 被static修饰的变量或者方法是优先于对象的存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问
7.static应用场景
因为static是被类的实例对象所共享,因此如果某个成员变量是被所以对象实例所共享的,那么这个成员变量就应该定义为静态变量 因此比较常见的static应用场景有: 修饰成员变量 修饰成员方法 静态代码块 修饰类【只能修饰内部类也就是静态内部类】 静态导包
.static注意事项
- 静态只能访问静态
- 非静态既可以访问非静态的,也可以访问静态的
5、流程控制语句
break,continue,return的区别及作用
break跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) continue跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) return程序返回,下次执行下面的代码(结束当前的方法 直接返回)
二、面对对象
1、面向对象概述
- 面向对象和面向过程的区别
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、linux/Unix等一般采用面向过程开发,性能时最重要的因素 缺点:没有面向对象以维护、易复用、易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护 缺点:性能比面向过程低
面向过程是具体化的,流程化的,解决一个问题,你需要一步步的分析,一步步的实现
面向对象是模型化的,你只需抽象一个类,这是一个封闭的盒子,在这里你拥有的数据也拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必一步一步的实现
面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了
2、面向对象的三大特征
面向对象的特征有哪几个方面
面向对象的特征主要有以下几个方面: 抽象: 抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么
封装: 封装是把一个对象的属性私有化,同时提供一些可以被外界方法问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。凡是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
继承: 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。 关于继承如下3点请记住:
1. 子类拥有父类的非private的属性和方法
2. 子类可以拥有自己属性和方法,机子类可以对父类进行扩展
3. 子类可以用自己的方式实现父类的方法
多态: 所谓多态就是指程序中定义的应用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用哪个类中实现的方法,必须在有程序运行期间才能决定。多态是同一个行为具有多个不同表现形式或形态的能力 在java中有两种形式可以实现多态:继承(多个子类对同一种方法的重写)和接口(实现接口并覆盖接口中的同一方法)。 其中java面向对象编程三大特性:封装、继承、多态 封装: 隐藏对象的属性和实现细节,仅对外提供访问方式,将变化隔离,便于使用,提高复用性和安全性。
多态性: 父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的扩展性
多态的实现: java实现多态有三个必要条件:继承、重写、向上转型。
继承:在多态中必须讯在继承关系的子类和父类
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法
向上转型:在多态中需要子类的引用赋给父类对象,只有这样该引用才能具备技能调用父类的方法和子类的方法
只有满足上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为
对于java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定调 用谁的成员方法,但是这个被调用的方法必须时在超类中定义过的,也就是说被子类覆盖的方法
3、类与接口
抽象类和接口的对比
抽象类是用来捕捉子类的通用特性的,接口时抽象方法的集合
从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范
相同点
接口和抽象类都不能实例化
都为与继承的顶端,用于被其他实现或者继承
都包含抽象方法。其子类都必须覆写这些抽象方法
不同点 行为模型应该总是通过接口而不是抽象类定义,所以通常是优选选用接口,尽量少用抽象类
选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能
普通类和抽象类的区别
- 普通类不能包含抽象方法,抽象类可以包含抽象方法
- 抽象类不能直接实例化,普通类可以直接实例化
抽象类能使用final修饰吗?
不能,定义抽象类就是让其它类继承的,如果定义为final该类就不能继承,这样彼此就会产生矛盾,所以final不能修饰抽象类
创建一个对象用什么关键字?对象实例与对象引用有何不同?
new关键字,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向0个或1个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有n个引用指向它
4、变量与方法
1.成员变量与局部变量的区别有哪些
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质讲,变量其实是内存中的一小块区域
作用域
成员变量:方法外部,类内部定义的变量
局部变量:只在某个范围内有效。(一般指得就是方法,语句体内)
存储位置
成员变量:随着对象得创建而存在,随着对象得消失而消失,储存在堆内存中。
局部变量:在方法被调用,或者语句被执行得时候存在,储存在栈内存中。当方法调用完,或者语句结束后,就自动释放。
初始值
成员变量:有默认初始值。
局部变量:没有默认初始值,使用前必须赋值
使用原则
在使用变量是需要遵循得原则行为:就近原则
首先在局部范围找,有就使用;接着在成员位置找
2.在调用子类得构造方法得作用是什么?
帮助子类做初始化工作
3.构造方法有哪些特性?
名字与类名相同;
没有返回值,但不能用void声明构造函数;
生成类的对象时自动执行,无需调用
4.静态变量和实例变量区别
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只会存在一份,在类的加载过程中,JVM只为静态变量分配内存空间。
实例变量:每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就会有几份成员变量。
5.静态变量与普通变量区别
static变量也称做静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类的初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。
6.静态方法和实例方法有何区别
静态方法和实例方法的区别主要体现在两个方面:
1.在外部调用静态方法是,可以使用"类名.方法"的方式,也可以使用"对象名.方法名"的
方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以不许创建对象。
2.静态方法在访问本类成员变量时,只允许访问静态成员(即静态成员和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
7.在一个静态方法内调用一个非静态成员为什么是非法的
由于静态方法可以不通过对象进行调用,因此静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员
5、内部类
1.什么是内部类
在java中,可以将一个类的定义方法另外一个类的定义内部,这就是内部类,内部类本身就是类的一个属性,与其他属性定义方式一致。
2.内部类的分类有哪些
内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类。
静态内部类
定义在类内部的静态类,就是静态内部类。
public class Outer {
private static int radius = 1;
static class StaticInner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
}
}
}
静态内部类可以访问外部类所有的静态变量,而不可以访问外部类的非静态变量;静态内部类的创建方式,new外部类,静态内部类(),
Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();
成员内部类
定义在类的内部,成员位置上的非静态类,就是成员内部类
public class Outer {
private static int radius = 1;
private int count =2;
class Inner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
System.out.println("visit outer variable:" + count);
}
}
}
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new内部类(),如下:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit()
局部内部类
定于在方法中的内部类,就是局部内部类。
public class Outer {
private int out_a = 1;
private static int STATIC_b = 2;
public void testFunctionClass(){
int inner_c =3;
class Inner {
private void fun(){
System.out.println(out_a);
System.out.println(STATIC_b);
System.out.println(inner_c);
}
}
Inner inner = new Inner();
inner.fun();
}
public static void testStaticFunctionClass(){
int d =3;
class Inner {
private void fun(){
// System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
System.out.println(STATIC_b);
System.out.println(d);
}
}
Inner inner = new Inner();
inner.fun();
}
}
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new内部类(),如下:
public static void testStaticFunctionClass(){
class Inner {
}
Inner inner = new Inner();
}
匿名内部类
匿名内部类就是没有名字的内部类,日常开发中使用的比较多。
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类" );
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service{
void method();
}
除了没有名字,匿名内部类还有以下特点:
- 匿名内部类必须继承一个抽象类或者实现一个接口
- 匿名内部类不能定义任何静态成员和静态方法
- 当所在的方法的形参需要被匿名内部类是使用时,必须声明为final
- 匿名内部类不能是抽象的,他必须实现继承的类或者接口的所有抽象方法
创建方式:
new 类/接口{
//匿名内部类实现部分
}
3.内部类的优点
我们为什么要使用内部类呢?因为它有一下优点、
- 一个内部类对象可以访问创建他的内部类对象,包括私有数据!
- 内部类不为同一个包的其他类所见,具有很好的封装性
- 内部类有效实现了"多重继承",优化java单继承的缺陷
- 匿名内部类可以很方便的定义回调
4.内部类有哪些应用场景
- 一些多算法场合
- 解决一些非面向对象的语句块
- 合适使用内部类,使得代码更加灵活和富有扩展性
- 当某个类除了它的内部类,不再用其他的类使用时
5.局部内部类和匿名内部类访问局部变量的时候,为什么变量要加上final?
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它的内部原理是什么的?
看一下代码
public class Outer {
void outMethod(){
final int a =10;
class Inner {
void innerMethod(){
System.out.println(a);
}
}
}
}
以上代码,为什么要加上final呢?是因为生命周期不一致,局部变量直接储存在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。
6.重写与重载
-
构造器(constructor)是否可被重写(override)
构造器不能被继承,因此不能被重写,但可以被重载
-
重载(Overload)和重写(Override)的区别。重载的方法是否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性
重载:发生在同一个类中,方法名相同参数列表不用(参数类型不同、个数不同、顺序不同),与方法时的多态性。
重写:发生在父子类中、方法名、参数列表必须相同,返回值小于等于父类,抛出异常小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private则子类中就不是重写
7.对象相等判断
-
==和equals的区别是什么
==:它的作用时判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
equals():它的作用也是判断两个对象是否相等,但它一般有两种情况:
情况一:类没有被覆盖equals方法。则通过equals()比较该类的两个对象时,等价于通过"=="比较这两个对象
情况二:类覆盖了equals方法。一般我们都覆盖equals方法来比较两个对象的内容是否相等,则返回true(认为这两个对象相等)
实例:
public class Outer { void outMethod(){ final int a =10; class Inner { void innerMethod(){ System.out.println(a); } } } }说明:
String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
-
hashCode与equals(重要)
HashSet如何检查重复
两个对象的hashCode()相同,则equals()也一定为true,对吗?
hashCode和equals方法的关系
面试官可能会问你:"你重写过hashCode和equals么,为什么要重写equals是必须重写hashCode方法"
hashCode()介绍
hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,这就意味着java中的任何类都包含hashCode()函数
散列表储存的是键值对,它的特点是:根据”键“快速检索出对应的值。这其中就利用了散列码(可以快速找到所需要的对象)
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入HashSet时,HashSet会先计算出对象的hashCode值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode值进行比较,如果没有相符的hashCode,HashSet会假设对象没有重复出现。但是如果发现有相同的hashCode值得对 象,这时就会调用equals方法来检查hashCode相等的对象是否真的相等。如果两者相同,HashSet就不会让其加入操作成功。如果 不同的话,就会重写散列到其他位置。这样我们就大大减少了equals的次数,相应就大大提高了执行速度。
hashCode()与equals的相关规定
如果两个对象相等,则hashCode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashCode值,他们也不一定相等的
因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
hashCode()默认行为是对堆上的对象产生独特值。如果就没有重写hashCode(),则该class的两个对象无论无何都不会相等(即使这两个对象指向相同数据)
8.值传递
-
当一个对象被当作参数传递一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递
是值传递。java语言得方法调用只支持参数得传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但是对对象引用的改变是不会影响到调用者的
-
为什么java中只有值传递
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用表示方法接收的是调用者提供的值,而按引用调用表示方法接受的是调用者提供的变量地址。一个方法可以修改传递引用对应的变量值,而不能修改传递值所对应的变量值。它用来描述各种程序设计语言中方法参数传递方式
java程序设计语言总是采用按值调用。也就是说,方法得到的是索引参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。
总结
java程序设计语言对对象采用的不是引用调用,实际上,对象引用时按值传递的
下面再总结一下java中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
- 一个方法可以改变一个对象的参数状态
- 一个方法不能让对象参数引用一个新的对象
3.值传递和引用传递有什么区别
值传递:指的就是在方法调用时,传递的参数时按值的拷贝传递,传递的是值得拷贝,也就是说传递后就互不相关了
引用传递:指得就是在方法调用时,传递的参数时按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值得引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)
9.JAVA包
-
JDK中常用得包有哪些
- java.lang:这个是系统的基础类;
- java.io:这里面是所有输入输出有关的类,比如文件操作等;
- java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
- java.net:这里面是与网络有关的类;
- java.util:这个是系统辅助类,特别是集合类;
- java.sql:这个是数据库操作的类。
三、IO流
1.java中IO流分为几种
- 按照流的流向分,可以分为输入流和输出流
- 按照操作单元划分,可以划分为字节流和字符流
- 按照流的角色划分为节点流和处理流
java IO流共涉及40多个类,这些看上去去很杂乱,但实际上很有规律,而且彼此之间存在着非常紧密的联系,java IO流的40多个类都是从如下4个抽象类基类中派生出来的
-
InputStream/Reader:所有的输入流的基类,前者是字节输入流,后者是字符输入流
-
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流
按操作方式分类的图:
按操作对象分类结构图:
2.BIO,NIO,AIO有什么区别?
简答
- BIO:Block IO同步阻塞式IO,就是我们平常使用的传统IO,他的特点就是模式简单使用方便,并发处理能力低。
- NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
3.File的常用方法有哪些?
- Files.exists():检测文件路径是否存在
- Files.createFile():创建文件
- Files.createDirectory():创建文件夹
- Files.delete():删除一个文件或目录
- Files.copy():复制文件
- Files.move():移动文件
- Files.size():查看文件个数
- Files.read():读取文件
- Files.write():写入文件
四、反射
1.什么式反射机制?
JAVA反射机制式运行在状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能成为java语言的反射机制
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行确定类型,绑定对象
2.反射机制优缺点
- 优点:运行期间类型的判断,动态加载类,提高代码灵活度。
- 缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多
3.反射机制的应用场景有哪些?
反射是框架设计的灵魂。
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明没有用,实际上由很多的设计、开发都于反射机制有关,例如模块化的开发,通过反射机制去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常的Spring/Hibernate等框架也大量用到了反射机制。
举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
4.JAVA获取反射的三种方法
-
通过new对象实现反射机制
-
通过路径实现反射机制
-
通过类名实现反射机制
public class Student { private int id; String name; protected boolean sex; public float score; }public class Get { //获取反射机制三种方式 public static void main(String[] args) throws ClassNotFoundException { //方式一(通过建立对象) Student stu = new Student(); Class classobj1 = stu.getClass(); System.out.println(classobj1.getName()); //方式二(所在通过路径-相对路径) Class classobj2 = Class.forName("fanshe.Student"); System.out.println(classobj2.getName()); //方式三(通过类名) Class classobj3 = Student.class; System.out.println(classobj3.getName()); } }
五、网络编程
1.计算机网络体系
2.网络协议是什么?
计算机网络要做到有条不紊地交换数据,就必须遵守一些事先约定好的规则,比如交换数据的格式、是否需要发送一个应答消息。这些规则被称为网络协议。
3.为什么要对网络协议分层?
- 简化问题难度和复杂度。由于各层之间独立,我们可以分割大问题为小问题
- 灵活性好,当其中一层的技术变化时,只要层间接口关系保持不变,其他层不受影响。
- 易于实现和维护
- 促进标准化工作。分开后,每层功能可以相对简单地被描述
网络协议分层的缺点:功能可以出现在多个层,产生额外开销
为了使不同体系结构的计算机网络都能互联,国际标准化组织 ISO 于1977年提出了一个试图使各种计算机在世界范围内互联成网的标准框架,即著名的开放系统互联基本参考模型 OSI/RM,简称为OSI。
OSI 的七层协议体系结构的概念清楚,理论也较完整,但它既复杂又不实用,TCP/IP 体系结构则不同,但它现在却得到了非常广泛的应用。TCP/IP 是一个四层体系结构,它包含应用层,运输层,网际层和网络接口层(用网际层这个名字是强调这一层是为了解决不同网络的互连问题),不过从实质上讲,TCP/IP 只有最上面的三层,因为最下面的网络接口层并没有什么具体内容,因此在学习计算机网络的原理时往往采用折中的办法,即综合 OSI 和 TCP/IP 的优点,采用一种只有五层协议的体系结构,这样既简洁又能将概念阐述清楚,有时为了方便,也可把最底下两层称为网络接口层。
四层协议,五层协议和七层协议的管理如下:
- TCP/IP是一个四层的体系结构,主要包括:应用层、运输层、网际层和网络接口层
- 五层协议的体系结构主要包括:应用层、运输层、网络层,数据链路层和物理层
- OSI七层协议模型主要包括是:应用层(Application)、表示层(Presentation)、会话层(Session)、运输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)
注:五层协议的体系结构只是为了介绍网络原理而设计的,实际应用还是 TCP/IP 四层体系结构。
4.TCP/IP协议族
应用层
应用层的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应 用进程间的通信和交互的规则
对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系 统DNS,支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等等。
运输层
运输层的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务应 用进程利用该服务传送应用层报文。
运输层主要使用以下两种协议
1.传输控制协议-TCP:提供面向连接的,可靠的数据传输服务
2.用户数据协议-UDP:提供无连接的,尽最大努力的数据传输服务(不保证数传输的可靠性)
UDP TRCP
是否连接 无连接 面向连接 是否可靠 可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制 连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信 传输方式 面向报文 面向字节流 首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节 场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输
每一个应用层(TCP/IP参考模型的最高层)协议一般都会使用到两个传输层协议之一:
运行在TCP协议上的协议:
- HTTP(Hypertext Transfer Protocol,超文本传输协议),主要用于普通浏览。
- HTTPS(HTTP over SSL,安全超文本传输协议),HTTP协议的安全版本。
- FTP(File Transfer Protocol,文件传输协议),用于文件传输。
- POP3(Post Office Protocol, version 3,邮局协议),收邮件用。
- SMTP(Simple Mail Transfer Protocol,简单邮件传输协议),用来发送电子邮件。
- TELNET(Teletype over the Network,网络电传),通过一个终端(terminal)登陆到网络。
- SSH(Secure Shell,用于替代安全性差的TELNET),用于加密安全登陆用。
运行在UDP协议上的协议:
- BOOTP(Boot Protocol,启动协议),应用于无盘设备。
- NTP(Network Time Protocol,网络时间协议),用于网络同步。
- DHCP(Dynamic Host Configuration Protocol,动态主机配置协议),动态配置IP地址。
运行在TCP和UDP协议上:
-
DNS(Domain Name Service,域名服务),用于完成地址查找,邮件转发等工作。
网络层
网络层的任务就是选择合适的网间路由和交换结点,确保计算机通信的数据及时传送。在发送数据时,网络层把运输层产生的报文段 或 用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称数据 报。
互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协 议 (Intert Prococol)和许多路由选择协议,因此互联网的网络层也叫做网际层或 IP 层。
数据链路层
数据链路层(data link layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链 路层的协议。
在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包 括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。
物理层
在物理层上所传送的数据单位是比特。 物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。
TCP/IP协议族
在互联网使用的各种协议中最重要和最著名的就是 TCP/IP 两个协议。现在人们经常提到的 TCP/IP 并不一定是单指 TCP 和 IP 这两个具体的协议,而往往是表示互联网所使用的整个 TCP/IP 协议族
划重点:
TCP(传输控制协议)和IP(网际协议) 是最先定义的两个核心协议,所以才统称为TCP/IP协议族
5.TCP的三次握手四次挥手
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务端保存的一份关于对方的信息,如ip地址、端口号等。
TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP头部。
一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)。通过三次握手建立一个链接,通过四次挥手来关闭一个连接。
当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。
TCP报文的头部结构
在了解TCP连接之前先来了解一下TCP报文的头部结构。
上图中有几个字段需要重点介绍下:
(1)序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。
(3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
- ACK:确认序号有效。
- FIN:释放一个连接。
- PSH:接收方应该尽快将这个报文交给应用层。
- RST:重置连接。
- SYN:发起一个新连接。
- URG:紧急指针(urgent pointer)有效。
需要注意的是:
不要将确认序号ack与标志位中的ACK搞混了。 确认方ack=发起方seq+1,两端配对。
三次握手
三次握手的本质是确认通信双方收发数据的能力
首先,我让信使运输一份信件给对方,对方收到了,那么他就知道了我的发件能力和他的收件能力是可以的。
于是他给我回信,我若收到了,我便知我的发件能力和他的收件能力是可以的,并且他的发件能力和我的收件能力是可以。
然而此时他还不知道他的发件能力和我的收件能力到底可不可以,于是我最后回馈一次,他若收到了,他便清楚了他的发件能力和我的收件能力是可以的。
这,就是三次握手,这样说,你理解了吗?
-
第一次握手:客户端要向服务端发起连接请求,首先客户端随机生成一个起始序列号ISN(比如是100),那客户端向服务端发送的报文段包含SYN标志位(也就是SYN=1),序列号seq=100。
-
第二次握手:服务端收到客户端发过来的报文后,发现SYN=1,知道这是一个连接请求,于是将客户端的起始序列号100存起来,并且随机生成一个服务端的起始序列号(比如是300)。然后给客户端回复一段报文,回复报文包含SYN和ACK标志(也就是SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发过来的序列号+1)。
-
第三次握手:客户端收到服务端的回复后发现ACK=1并且ack=101,于是知道服务端已经收到了序列号为100的那段报文;同时发现SYN=1,知道了服务端同意了这次连接,于是就将服务端的序列号300给存下来。然后客户端再回复一段报文给服务端,报文包含ACK标志位(ACK=1)、ack=301(服务端序列号+1)、seq=101(第一次握手时发送报文是占据一个序列号的,所以这次seq就从101开始,需要注意的是不携带数据的ACK报文是不占据序列号的,所以后面第一次正式发送数据时seq还是101)。当服务端收到报文后发现ACK=1并且ack=301,就知道客户端收到序列号为300的报文了,就这样客户端和服务端通过TCP建立了连接。
四次挥手
四次挥手的目的是关闭一个连接
比如客户端初始化的序列号ISA=100,服务端初始化的序列号ISA=300。TCP连接成功后客户端总共发送了1000个字节的数据,服务端在客户端发FIN报文前总共回复了2000个字节的数据。
- 第一次挥手:当客户端的数据都传输完成后,客户端向服务端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)。需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号。
- 第二次挥手:服务端收到客户端发的FIN报文后给客户端回复确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号1101+1)、序列号seq=2300(300+2000)。此时服务端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。
- 第三次挥手:服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)。
- 第四次挥手:客户端收到服务端发的FIN报文后,向服务端发出确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102。注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接。而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,所以服务端结束TCP连接的时间要比客户端早一些。