Java面试题总结

一、Java语法

Java和 c++

不同点:

解释型,编译型

纯面向对象,面向对象兼顾面向过程

更加安全

单继承,多继承

垃圾回收(finalize()->析构函数)

java继承,封装,多态

1.封装是什么:隐藏对象的属性和实现细节,仅仅对外提供接口,控制在程序中属性的读和写的访问级别。将数据和行为有机结合在一起,形成“类”

2.封装有什么用

使用者->简化编程,只要会使用外部暴露的接口即可。

java代码->增强安全性,规定特定的访问权限来使用类的成员。

3.封装的基本要求:把所有的字段都私有化,提供getset方法,如果有一个带参的构造器,必须提供不带参的构造器。

a.继承是什么:先抽象提取,形成一个类,基类A,如果类B要使用基类的属性和方法,需要使用extends关键字继承

b.继承的好处:实现代码复用,java不支持多继承,开发中应该少用继承,降低程序的耦合性,多组合少继承

c.继承的基本特征:子类不能继承父类private修饰成员变量和方法,但是子类可以重写父类的方法。

A:多态:编译时多态 + 运行时多态,

B: 方法的重载:重载是指同一个类中有多个同名方法,但这些方法的有着不同的参数个数参数类型参数顺序,因此在编译期间就确定到底调用哪个方法,他是编译时多态

C: 方法的覆盖(重写):子类可以覆盖父类的方法,相同方法不同表现形式,基类的引用变量不仅可以指向基类的实例对象,也可以指向子类的实例对象

重载和重写

Overload是重载的意思,Override是覆盖的意思,也就是重写。

\1. 重载:同一个类中可以有多个名称相同的方法,其参数列表各不相同。

\2. 重写:子类中的方法可以与父类中的某个方法的名称和参数完全相同。

1.通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。

2.子类覆盖父类的方法时,只能比父类抛出更少的异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。

3.子类方法的访问权限只能比父类的更大,不能更小。

4.如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。

问:如果两个方法的参数列表完全一样,是否可以让它们的返回值不同来实现重载

答:这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用map.remove(key)方法时,虽然remove方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。

即:不能通过访问权限、返回类型、抛出的异常进行重载;

构造方法能否被重写和重载?

构造方法是不能被重写的。构造方法可以被重载。

面向对象与面向过程的区别

1)认识问题角度:面向对象:死物 受规律被动操控 面向对象:活物 主动交互

2)解决问题方法:面向对象:函数 面向对象:对象

3)解决问题的中心思想:

面向过程:how 怎么样(流程被疯转到函数里,how如何 就是过程)

面向对 象:who,谁来做(谁,就是对象,如何做是他的事,多个对象合作完成一件事)

4)解决问题的步骤:面向过程:先具体逻辑细节,后抽象问题整体 面向函数:先抽象问题整体,在 具体实现

面向对象设计原则

单一职责原则(SRP)

开放封闭原则(OCP)

里氏替换原则(LSP)

依赖倒置原则(DIP)

接口隔离原则(ISP)

1.单一职责原则 —— 核心:高内聚,低耦合,分工明确,各司其职

2.开闭原则 —— 核心:开放扩展,关闭修改

3.里氏替换原则

——任何父类出现的地方,都可以用子类代替,子类方法的输入参数应该比相应父类方法的输入参数范围更宽松,输出结果 范围更小

4.依赖倒转原则 —— 核心:不依赖具体,依赖抽象

5.接口分离原则 —— 核心:接口的功能应该单一

6.合成复用原则 —— 核心:减少继承,增加组合

7.迪米特原则 —— 核心:一个类应该只关注自己的逻辑,尽量少的设计其他类的实现——最少知道

JDK 和 JRE

JDK(Java Development Kit )JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境。SDK一般指软件开发包,可以包括函数库、编译程序等。 JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。 JRE( Java Runtime Environment) 是指Java的运行环境,包含了java虚拟机,java基础类库 是面向Java程序的使用者,而不是开发者。

hashCode +equals

\2. equals在Object中与==的作用都是比较引用(地址值)是否相等。一般情况下会被重写,例如比较字符串比较的是字符串的值。没有重写的话就是比较的是地址是否相等。

\3. hashCode()根据这个对象内存储的数据及对象的一些特征来做散列返回一个有符号的 32 位哈希值,所以 hashCode() 方法返回的是一个散列值,而对于一个散列来说不同的内容也是可能会出现相同的散列值

hashCode方法的使用涉及到哈希表,在比较两个元素是否相同时先比较hashcode如果相同在比较equals,如果都相同,则是同一个元素

StringBuffer和StringBuilder

String类是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

创建了一个 String 类的对象之后,很难对她进行增、删、改的操作,为了解决这个弊端,Java 语言就引入了 StringBuffer 类。StringBuffer 的内部实现方式和 String 不同,StringBuffer 在进行字符串处理时,不用生成新的对象,内存的使用上 StringBuffer 要优于 String 类。

在 JDK 5.0 之后,引入了 StringBuilder 类

1.对于操作效率而言,一般来说,StringBuilder > StringBuffer > String;

2.对于线程安全而言,StringBuffer 是线程安全的;而 StringBuilder 是非线程安全的;

3.对于频繁的字符串操作而言,无论是 StringBuffer 还是 StringBuilder,都优于 String。

Java程序的种类

(a)内嵌于Web文件中,由浏览器来观看的_Applet

(b)可独立运行的 Application

(c)服务器端的 Servlets

Application ―Java应用程序”是可以独立运行的Java程序。由Java解释器控制执行。 Applet ―Java小程序”不能独立运行(嵌入到Web页中)。由Java兼容浏览器控制执行。

Serverlets 是Java技术对CGI 编程的解决方案。 是运行于Web server上的、作为来自于Web browser 或其他HTTP client端的请求和在server上的数据库及其他应用程序之间的中间层程序。 Serverlets的工作是: 读入用户发来的数据(通常在web页的form中) 找出隐含在HTTP请求中的其他请求信息(如浏览器功能细节、请求端主机名等。) 产生结果(调用其他程序、访问数据库、直接计算) 格式化结果(网页) 设置HTTP response参数(如告诉浏览器返回文档格式) 将文档返回给客户端。

== 和 equals 的区别

== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。 equals用来比较内容是否相等,默认equals方法返回的是==的判断。

hashCode()相同,则 equals()也一定为 true?

不对;

hashCode() 的作用是获取散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。 在散列表中, 1、如果两个对象相等,那么它们的hashCode()值一定要相同; 2、如果两个对象hashCode()相等,它们并不一定相等。

为什么重载了equals方法之后需要去重载hashCode方法?

为了保证Hash的时候调用对象的equals方法可以映射到同一个位置。

关键字:

静态块:用static申明,JVM加载类时执行,仅执行一次 构造块:类中直接用{}定义,每一次创建对象时执行 执行顺序优先级:静态块>main()>构造块>构造方法

分类

Java语言提供了很多修饰符,大概分为两类:

  1. 访问权限修饰符

  2. 非访问权限修饰符

访问权限修饰符

  1. public:所有的类可见。

  2. protected:同包可见,不同包子类可见。

  3. default:同包可见,不同的包子类不可见。

  4. private:同类可见。

非访问权限修饰符

  1. static:类方法和类变量。

  2. final:类不能够被继承,方法不能被重写,变量为常量。

  3. abstract:抽象类和抽象方法。

  4. synchronized 用于多线程的同步。

  5. volatile :访问时,强制从共享内存中读取该成员变量的值。变化时,强制将变化值回写到共享内存。结果保证了可见性。

  6. transient:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。

内部类的分类

包括四种:成员内部类,局部内部类,匿名内部类,静态内部类 。

 

 

1.成员内部类

(1)该类像是外部类的一个成员,可以无条件的访问外部类的所有成员属性和成员方法(包括private成员和静态成员);

(2)同名的成员变量采用就近原则。要访问外部类中的成员,外部类.this.成员变量 /外部类.this.成员方法

(3)外部类中访问成员内部类的成员,要创建一个成员内部类的对象,再通过指向这个对象的引用来访问;

(4)成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象;

(5)访问权限:全有

外部类只能被public和包访问两种权限修饰。

2.局部内部类

(1)定义在一个方法或者一个作用域里,访问仅限于方法内或者该作用域内;

(2)地位等同方法里面的一个局部变量一样,无修饰符的。

3.匿名内部类

(1)一般使用匿名内部类的方法来编写事件监听代码

(2)匿名内部类是不能有访问修饰符和static修饰符的;

(3)匿名内部类是唯一一种没有构造器的类

(4)匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

匿名内部类的创建格式为: new 父类构造器(参数列表)|实现接口(){

//匿名内部类的类体实现

}

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。

3、匿名内部类中不能存在任何的静态成员变量和静态方法。

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

4.静态内部类

(1)静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似;

(2)不能使用外部类的非static成员变量或者方法。

外部类修饰符

  • public(访问控制符)一个程序的主类必须是公共类。

  • default(访问控制符)

  • abstract(非访问控制符)将一个类声明为抽象类,抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充,抽象类可以包含抽象方法和非抽象方法。

  • fina表示它不能被其他类继承。

注意:

1.protected 和 private 不能修饰外部类,是因为外部类放在包中,只有两种可能,包可见和包不可见。

  1. final 和 abstract不能同时修饰外部类,因为该类要么能被继承要么不能被继承,二者只能选其一。

3.不能用static修饰类,因为类加载后才会加载静态成员变量。所以不能用static修饰类和接口,因为类还没加载,无法使用static关键字。

内部类修饰符

内部类与成员变量地位一直,所以可以public,protected、default和private,同时还可以用static修饰,表示嵌套内部类,不用实例化外部类,即可调用。

方法修饰符

  1. public(公共控制符),包外包内都可以调用该方法。

  2. protected(保护访问控制符)指定该方法可以被它的类和子类进行访问。

  3. default(默认权限),指定该方法只对同包可见,对不同包(含不同包的子类)不可见。

  4. private(私有控制符)指定此方法只能有自己类等方法访问,其他的类不能访问(包括子类),非常严格的控制。

  5. final ,指定方法已完备,不能再进行继承扩充。

  6. static,指定不需要实例化就可以激活的一个方法,即在内存中只有一份,通过类名即可调用。

  7. synchronize,同步修饰符,在多个线程中,该修饰符用于在运行前,对它所属的方法加锁,以防止其他线程的访问,运行结束后解锁。

  8. native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。

  9. abstract ,抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成 final 和 static。 任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。 抽象方法的声明以分号结尾,例如:public abstract sample();。

成员变量修饰符

  • public(公共访问控制符),指定该变量为公共的,它可以被任何对象的方法访问。

  • protected(保护访问控制符)指定该变量可以被自己的类和子类访问。在子类中可以覆盖此变量。

  • default(默认权限),指定该变量只对同包可见,对不同包(含不同包的子类)不可见。

  • private(私有访问控制符)指定该变量只允许自己的类的方法访问,其他任何类(包括子类)中的方法均不能访问。

  • final,最终修饰符,指定此变量的值不能变。

  • static(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类。

  • transient(过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量。不持久化。

  • volatile(易失修饰符)指定该变量可以同时被几个线程控制和修改,保证两个不同的线程总是看到某个成员变量的同一个值。

final 和 static 经常一起使用来创建常量。

局部变量修饰符

only final is permitted。

为什么不能赋予权限修饰符?

因为局部变量的生命周期为一个方法的调用期间,所以没必要为其设置权限访问字段,既然你都能访问到这个方法,所以就没必要再为其方法内变量赋予访问权限,因为该变量在方法调用期间已经被加载进了虚拟机栈,换句话说就是肯定能被当前线程访问到,所以设置没意义。

为什么不能用static修饰

我们都知道静态变量在方法之前先加载的,所以如果在方法内设置静态变量,可想而知,方法都没加载,你能加载成功方法内的静态变量?

接口

接口修饰符

接口修饰符只能用public、default和abstract。

不能用final、static修饰。

接口默认修饰为abstract。

接口中方法修饰符:只能用 public abstract修饰,如果都不写,默认是public abstract。

注意:在Java1.8之后,接口可以用static来修饰

swtich:switch支持 int及以下(char, short, byte)&包装类型,String, Enum(整数表达式或者枚举常量(更大字体))String类型是Java7开始支持。

final

1、final成员变量表示常量,如果final指针,那么指针地址不可以改变,但是指针指向的对象可以改变 2、final类不能继承 3、final方法不能重写,但是可以继承 3、final不能修饰构造函数

static

作用于方法上:方法属于类,不属于类的实例对象;Static方法不需要实例对象就可以通过类名调用,且Static方法中不能有实例成员。

作用于变量上:变量属于类,可以用类型引用。

作用于类上:static类只能有static成员。

在什么时候执行:在代码

非static:

作用于方法上:属于类的实例对象,可以有static成员。

作用于变量上:属于实例对象或者是局部变量,创建类的实例对象才能引用。

作用于类上:可以有Static成员也可以有非Static成员。

static是在编译的时候被绑定,加载的时候就会被执行的。按顺序执行static变量和static代码块。

Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

final finalize finally ?

final: 类被继承,final变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。方法不能重载。

finalize:

作用:垃圾收集器将对象从内存中清除出去之前做必要的清理工作。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。

时机:由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

来源:在 Object 类中定义。

finally:异常处理时提供 finally 块来执行任何清除操作。

finally语句的执行 是在try / catch 中 return语句 执行之后,还是返回之前呢?

1:return语句执行之后返回之前 执行的

2:finally代码块中的return语句覆盖try代码块中的return语句

3:如果finally代码块,没有return语句对try代码块的return语句进行覆盖的话,那么原来的返回值会因为finally代码块中的修改,会导致返回值可能改变也可能不变。

如果返回值类型是传值类型:则不会改变返回值 /

如果返回值是传址类型:会对返回值造成影响(比如在finally代码块中对map进行添加时,key相同时,会进行添加覆盖)

传址:对象 / 数组 传值:8大基本数据类型 及其 包装类,字符常量

try代码块中的语句在异常情况下 不会继续向下执行。

Math.round()

Math.round()的原理是对传入的参数+0.5之后,再向下取整得到的数就是返回的结果,返回值为long型。这里的向下取整是说取比它小的第一个整数或者和它相等的整数。-1.5+0.5=-1向下取整为-1,。 向下取整:Math.floor(); 向上取整:Math.ceil();

基本类型

8个 1、字符类型:byte,char 2、基本整型:short,int,long 3、浮点型:float,double 4、布尔类型:boolean

String、StringBuffer、StringBuilder

string是值传入,不是引用传入。JVM运行程序主要的时间耗费是在创建对象和回收对象上。

String JDK1.0:字符常量被final修饰,当String初始化的时候等号右边以多个字符串常量拼接(+)来初始化时,效率等于用一个字符串初始化

StringBuffer JDK1.0:字符变量,被synchronize修饰,线程安全

StringBuilder JDK1.5:字符变量,无修饰 执行速度:在这方面运行速度快慢为:StringBuilder > StringBuffer > String

String:适用于少量的字符串操作的情况 StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况 StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

String str="i"与 String str=new String("i")一样吗

字符串反转

直接用StringBuffer自带的方法reverse() 把字符串转换成字符数组首位对调位置

String 常用方法

char charAt(int index);//返回指定索引处的 char 值。 int compareTo(String anotherString) ;//按字典顺序比较两个字符串。 String concat(String str) ;将指定字符串连接到此字符串的结尾。 boolean contains(CharSequence s);当且仅当此字符串包含指定的 char 值序列时,返回 true。 boolean endsWith(String suffix) ;//测试此字符串是否以指定的后缀结束。 boolean equals(Object anObject) ;//将此字符串与指定的对象比较。 byte[] getBytes();//使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 int indexOf(int ch);//返回指定字符在此字符串中第一次出现处的索引。 int lastIndexOf(int ch) ;//返回指定字符在此字符串中最后一次出现处的索引。 int length();//返回此字符串的长度。 String[] split(String regex);//根据给定正则表达式的匹配拆分此字符串。 boolean startsWith(String prefix);//测试此字符串是否以指定的前缀开始。 char[] toCharArray();//将此字符串转换为一个新的字符数组。 String toUpperCase();//使用默认语言环境的规则将此 String 中的所有字符都转换为大写。 String trim();//返回字符串的副本,忽略前导空白和尾部空白。 String toLowerCase(Locale locale);//使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。

substring** 方法将返回一个包含从 start 到最后(不包含 end )的子字符串的字符串。

构造方法 描述
StringBuilder() 创建一个容量为16的StringBuilder对象(16个空元素)
StringBuilder(CharSequence cs) 创建一个包含cs的StringBuilder对象,末尾附加16个空元素
StringBuilder(int initCapacity) 创建一个容量为initCapacity的StringBuilder对象
StringBuilder(String s) 创建一个包含s的StringBuilder对象,末尾附加16个空元素

不可变类:说的是一个类一旦被实例化,就不可改变自身的状态。常见的比如String和基本数据类型的包装类,对于这种不可变类,一旦在进行引用传递的时候,形参一开始就和实际参数指向的不是一个地址,所以在方法中对形参的改变,并不会影响实际参数。

抽象类必须要有抽象方法吗

抽象类可以不包含抽象方法,包含抽象方法的类一定是抽象类。

抽象类的特点

1、抽象类不能被实例化。 2、抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。 3、抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体 4、含有抽象方法的类必须申明为抽象类 5、抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类。

抽象类能使用 final 修饰吗

不能

java类一旦被声明为abstract(抽象类),必须要继承或者匿名(其实匿名也是种继承)才能使用。 而final则是让该类无法被继承,所以final是必然不能和abstract同时声明的 但是private呢?一个类被声明为private的时候,它当然是内部类,内部类是可以被它的外部类访问到的,所以,可以继承,private和abstract不冲突。

抽象类和接口的区别

①在接口中不可以有构造方法

A. 构造方法用于初始化成员变量,但是接口成员变量是常量,无需修改。接口是一种规范,被调用 时,主要关注的是里边的方法,而方法是不需要初始化的,

B. 类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器链的调用次序

C. 构造器是属于类自己的,不能继承。因为是纯虚的,接口不需要构造器。

②在抽象类中 可以有构造方法。

在抽象类中可以有构造方法,只是不能直接创建抽象类的实例对象,但实例化子类的时候,就会初始化父类,不管父类是不是抽象类都会调用父类的构造方法,初始化一个类,先初始化父类。

接口和抽象类

关于抽象类

JDK 1.8以前,抽象类的方法默认访问权限为protected

JDK 1.8时,抽象类的方法默认访问权限变为default

关于接口

JDK 1.8以前,接口中的方法必须是public的

JDK 1.8时,接口中的方法可以是public的,也可以是default的

JDK 1.9时,接口中的方法可以是private的

抽象类:

1.在Java中属于一种继承关系,一个类只能继承一次。

2.有自己的数据成员,可以有非抽象方法。

3.抽象类表示的关系是is-a。

4.实现抽象类和接口都必须实现其中的所有方法,抽象类中可以有非抽象方法。

5.抽象类中变量默认的方法是friendly型,其值可以在子类中重新定义,也可以重新赋值。

接口:

1.在Java中也属于一种继承关系,但是一个类可以实现多个接口。

2.必须是static final修饰的数据成员,所有的成员方法都是抽象的。

3.接口表示的关系是 like-a。

4.接口中不能有实现方法(除非用default关键字修饰)

5.接口中定义的变量默认是public static final型,所以在实现类不能改变其值,不能重新定义;方法默认是 public abstract。

java 中 IO 流分为几种

BIO、NIO、AIO

Java BIO : 同步并阻塞,客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

Java NIO : 同步非阻塞,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知务器应用去启动线程进行处理。

NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)

AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。

适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。

NIO实现原理

阻塞与非阻塞

传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write() 时,该线程阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。

因此,在完成网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。

Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。

同步与异步

如果调用方需要保持等待直到IO操作完成进而通过返回 获得结果,则是同步的;如果调用方在IO操作的执行过程中不需要保持等待,而是在操作完成后被动的接受(通过消息或回调)被调用方推送的结果,则是异步的。

选择器Selectors

选择器(Selector) 是 SelectableChannle 对象的多路复用器,Selector 可以同时监控多个 SelectableChannel 的 IO 状况,也就是说,利用 Selector 可使一个单独的线程管理多个 Channel。Selector 是非阻塞 IO 的核心.使用单个线程来处理多个channel相对于多线程来处理多个通道的好处显而易见;节省了开辟新线程和不同的线程之间切换的开销。

两个关键类: Channel和Selector。他们是NIO的核心概念。

可以将Channel比作汽车,selector比作车辆调度系统,他负责每辆车运行状态。Buffer类可以比作车上的座位.

通俗解释

首先创建Selector选择器,创建服务端Channel并绑定到一个Socket对象,并将该通信信道注册到选择器上设置为非阻塞模式。然后就可以调用Selector的selectedKeys方法检查已经注册的所有通信信道是否有事情发生,如有事情发生会返回所有selectionKey,通过这个对象的Channel方法就可以去的这个通信信道的对象,从而读取通信数据Buffer。

通常情况下,一个线程以阻塞方式专门负责监听客户端连接请求,另一个线程专门负责处理请求,这个处理请求的线程才会真正采用NIO的方式。

让Selector来监控一个集合中的所有的通道,当有的通道数据准备好了以后,就可以直接到这个通道获取数据。当线程2去问该线程时,它会知道告诉我们通道 N 已经准备好了,而不需要线程2去轮询

AIO异步IO

AIO 最主要的特点就是回调。

NIO 很好用,它解决了阻塞式 IO 的等待问题,但是它的缺点是需要我们去轮询才能得到结果。

而异步 IO 可以解决这个问题,线程只需要初始化一下,提供一个回调方法,然后就可以干其他的事情了。当数据准备好以后,系统会负责调用回调方法。

Files的方法?java.nio.file.Files

Files类是java.nio.file.Files,其提供了几种操作文件系统中的文件的方法。例如:Files.exists();Files.createDirectory();Files.copy();Files.move();Files.delete()

Java自动装箱

自动拆装箱:

1、基本型和基本型封装型进行“==”运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true; 2、两个Integer类型进行“==”比较,如果其值在-128至127,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关。 3、两个基本型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true 4、基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行3中的比较。

数据类型转换:

float占4个字节为什么比long占8个字节大呢,因为底层的实现方式不同。

浮点数的32位并不是简单直接表示大小,而是按照一定标准分配的

第1位,符号位,即S

接下来8位,指数域,即E。

剩下23位,小数域,即M,取值范围为[1 ,2 ) 或[0 , 1)

然后按照公式: V=(-1)^s * M * 2^E

也就是说浮点数在内存中的32位不是简单地转换为十进制,而是通过公式来计算而来,通过这个公式虽然,只有4个字节,但浮点数最大值要比长整型的范围要大

操作数 1 类型 操作数 2 类型 转换后的类型
byte 、 short 、 char int int
byte 、 short 、 char 、 int long long
byte 、 short 、 char 、 int 、 long float float
byte 、 short 、 char 、 int 、 long 、 float double double

总体主要分为两个方面 ①比较的是值 一、基本数据类型与引用数据类型进行比较时,引用数据类型会进行拆箱,然后与基本数据类型进行值的比较 举例: int i = 12; Integer j = new Integer(12); i == j 返回的是true 二、引用数据类型与基本数据类型进行比较(equals方法),基本数据类型会进行自动装箱,与引用数据类型进行比较,Object中的equals方法比较的是地址,但是Integer类已经重写了equals方法,只要两个对象的值相同,则可视为同一对象,具体看API文档,所以这归根到底也是值的比较! 举例: int i = 12; Integer j = new Integer(12); j.equals(i) 返回的是true ②比较的是地址 一、如果引用数据类型是这样 Integer i = 12;直接从常量池取对象,这是如果数值是在-128与127之间,则视为同一对象,否则视为不同对象 举例: Integer i = 12; Integer j = 12; i == j 返回的是true Integer i = 128; Integer j = 128; i == j 返回的是false 二、如果引用数据类型是直接new的话,不管值是否相同,这时两个对象都是不相同的,因为都会各自在堆内存中开辟一块空间 举例: Integer i =new Integer(12); Integer j = new Integer(12); i == j 这时返回的是false 三、从常量池取对象跟new出来的对象也是不同的 举例: Integer i = 12; Integer j = new Integer(12) i == j 这时返回的是false,因为第二个语句其实已经是new了两个对象了!!!

  1. java 1.5 开始的自动装箱拆箱机制其实是编译时自动完成替换的,装箱阶段自动替换为了 valueOf 方法,拆箱阶段自动替换为了 xxxValue 方法。

  2. 对于 Integer 类型的 valueOf 方法参数如果是 -128~127 之间的值会直接返回内部缓存池中已经存在对象的引用,参数是其他范围值则返回新建对象;

  3. 而 Double 类型与 Integer 类型类似,一样会调用 Double 的 valueOf 方法,但是 Double 的区别在于不管传入的参数值是多少都会 new 一个对象来表达该数值(因为在指定范围内浮点型数据个数是不确定的,整型等个数是确定的,所以可以 Cache)。

注意:Integer、Short、Byte、Character、Long 的 valueOf 方法实现类似,而 Double 和 Float 比较特殊,每次返回新包装对象,对于两边都是包装类型的比较 == 比较的是引用,equals 比较的是值,对于两边有一边是表达式(包含算数运算)则 == 比较的是数值(自动触发拆箱过程),对于包装类型 equals 方法不会进行类型转换。

Java中包装类缓存

Integer包装类在自动装箱的过程中,是有缓冲池的。对于值在-128~127之间的数,会放在内存中进行重用;对于大于或者小于这个范围的数,有使用的时候都会new出一个新的对象。

java使用该机制是为了达到最小化数据输入和输出的目的,这是一种优化措施,提高效率(可以设置系统属性 java.lang.Integer.IntegerCache.high 修改缓冲区上限,默认为127。参数内容应为大于127的十进制数形式的字符串,否则将被忽略。取值范围为127-Long.MAX_VALUE,但是用时将 强转为int。当系统中大量使用Integer时,增大缓存上限可以节省小量内存)。

其他包装类缓存:Boolean(全部缓存)、Byte(全部缓存)、Character(<= 127缓存)、Short(-128~127缓存)、Long(-128~127缓存)、Float(没有缓存)、Double(没有缓存)。

内存泄漏

内存泄露的概念:可达+无用=无法回收

存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

JVM工具

jps——查看java进程pid;

jinfo——查看java进程启动参数;

jstack——查看java进程线程栈信息;

jstat——统计java进程的内存占用和回收情况;

jmap——导出JVM堆进行分析,强制产生Full GC;

2、其他工具

jmeter——用于压测模拟线上业务请求;

MAT——用于分析JVM堆是否有内存泄露;

GDB——用于分析内存指定区域存放的具体信息;

gperftools——谷歌的内存问题排查工具,可以用来根据内存分配情况;

3、Linux命令

top——初步定为进程占用的内存,区分虚拟内存和实际占用内存;

free——查看系统内存消耗情况;

pmap——定位进程的内存占用情况;

内存泄露如何定位

泛型

泛型的实现原理:

Java的泛型是伪泛型,Java中的泛型基本上都是在编译器这个层次来实现的,在生成的Java字节码中是不包含泛型中的类型信息的,使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉;

泛型的使用场景:

JAVA中泛型的使用:java中集合使用了泛型,Future<T>,WeakReference<T>,Class<T>也都使用了泛型

一些使用场景:

不想写多个重载函数的场景。

约束对象类型的场景,可以定义边界(T extends ...),如JDK集合List,Set。

用户希望返回他自定义类型的返回值场景,如Json返回Java bean。

在用反射的应用中,也经常会用到泛型,如Class<T>。

对网页,对资源的分析,返回场景,一般都有泛型。

泛型擦除:

类间关系

USES-A:依赖关系,A类会用到B类,这种关系具有偶然性,临时性。但B类的变化会影响A类。这种在代码中的体现为:A类方法中的参数包含了B类。

关联关系:A类会用到B类,这是一种强依赖关系,是长期的并非偶然。在代码中的表现为:A类的成员变量中含有B类。

HAS-A:聚合关系,拥有关系,是关联关系的一种特例,是整体和部分的关系。比如鸟群和鸟的关系是聚合关系,鸟群中每个部分都是鸟。

IS-A:表示继承。父类与子类,这个就不解释了。

要注意:还有一种关系:组合关系也是关联关系的一种特例,它体现一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分的关系,但这种整体和部分是不可分割的。

构造方法

本类的构造方法可以用this.相互调用

构造方法也是类的方法,可以在创建对象时为成员变量赋值

构造函数不能被继承,构造方法只能被显式或隐式的调用。

构造方法是一种特殊的方法,具有以下特点。

(1)构造方法的方法名必须与类名相同。

(2)构造方法没有返回类型,也不能定义为void,在方法名前面不声明方法类型。

(3)构造方法的主要作用是完成对象的初始化工作,它能够把定义对象时的参数传给对象的域。

(4)一个类可以定义多个构造方法,如果在定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,这个构造器不执行任何代码。

(5)构造方法可以重载,以参数的个数,类型,顺序。

new String("abc")时,其实会先在字符串常量区生成一个abc的对象,然后new String()时会在堆中分配空间,然后此时会把字符串常量区中abc复制一个给堆中的String

在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解,子类对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。就像有些东西你可能拥有,但是你并不能使用。所以子类对象是绝对大于父类对象的,所谓的子类对象只能继承父类非私有的属性及方法的说法是错误的。可以继承,只是无法访问到而已。

父类没有无参的构造函数,所以子类需要在自己的构造函数中显式调用父类的构造函数, 添加 super("nm"); 否则报错:

如果子类构造器没有显示地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中有没有显示地调用超类的其他构造器,则Java编译器将报告错误。使用super调用构造器的语句必须是子类构造器的第一条语句。——p153《Java核心技术卷I》

自加

count = count++ 原理是 temp = count; count = count+1 ; count = temp; 因此count始终是0 这仅限于java 与c是不一样的

符号:

>>带符号右移
>>>无符号右移
Java中的位运算符:
​
>>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;
​
>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。

标识符

标识符是以字母开头的字母数字序列:

数字是指0~9,字母指大小写英文字母、下划线(_)和美元符号($),也可以是Unicode字符集中的字符,如汉字;

字母、数字等字符的任意组合,不能包含+、- *等字符;

不能使用关键字;

大小写敏感,不可以数字开头

实例方法

实例方法可以调用超类公有实例方法

实例方法可以直接调用超类的公有类方法

实例方法可以通过类名调用其他类的类方法

实例方法可直接调用本类的类方法

关于抽象类和抽象方法

抽象类是为了将一系列事务抽象出来,比如人有各种各样的人,那么我们将Person抽象出来作为一个父类,然后派生出子类,代表各种各样的人,而每个人都有名字,子类中都有getName()方法,但是Person类对名字的具体实现一无所知,所以我们将getName()声明为一个抽象方法,这样就可以不用实现这个方法了

public` `abstract` `String getName();

同时,getName()所属的方法也需要声明为抽象方法

abstract` `class` `Person{` `public` `abstract` `String getName();` `......` `}
  • 当然,抽象类也可以拥有普通的成员变量和方法。

  • 抽象类起到的是一种占位的作用,具体的实现交给子类,所以抽象类不能被实例化,比如 new Person("lxb") 就是错误的。

  • 还需要注意一点就是,一个类只能继承一个抽象类,这个很好理解,小明是一个人,小明只能继承Person这个抽象类,而不能同时继承Fish这个抽象类,因为小明只是人,不是鱼。

java只有值传递

当一个类导入了多个不同包下相同类名的类时,会编译无法通过

执行对象实例化过程中遵循多态特性 ==> 调用的方法都是将要实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法

管道:管道实际上是一种固定大小的缓冲区,管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。它类似于通信中半双工信道的进程通信机制,一个管道可以实现双向 的数据传输,而同一个时刻只能最多有一个方向的传输,不能两个方向同时进行。管道的容 量大小通常为内存上的一页,它的大小并不是受磁盘容量大小的限制。当管道满时,进程在 写管道会被阻塞,而当管道空时,进程读管道会被阻塞

父类静态域——》子类静态域——》父类成员初始化——》父类构造块——》1父类构造方法——》2子类成员初始化——》子类构造块——》3子类构造方法;初始化中,类的静态变量和静态代码块的执行顺序是根据他们定义的顺序来的,优先级是平等的。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。另外,接口和抽象类在方法上有区别:

1.抽象类可以有构造方法,接口中不能有构造方法。

2.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

3.抽象类中可以有普通成员变量,接口中没有普通成员变量

\4. 抽象类中的抽象方法的访问类型可以是public,protected和默认类型

\5. 抽象类中可以包含静态方法,接口中不能包含静态方法

\6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型

\7. 一个类可以实现多个接口,但只能继承一个抽象类。二者在应用方面也有一定的区别:接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码。

总结一下

\1. 一个子类只能继承一个抽象类,但能实现多个接口

\2. 抽象类可以有构造方法,接口没有构造方法

\3. 抽象类可以有普通成员变量,接口没有普通成员变量

\4. 抽象类和接口都可有静态成员变量,抽象类中静态成员变量访问类型任意,接口只能public static final(默认)

\5. 抽象类可以没有抽象方法,抽象类可以有普通方法,接口中都是抽象方法

\6. 抽象类可以有静态方法,接口不能有静态方法

\7. 抽象类中的方法可以是public、protected;接口方法只有public

为什么java底层使用快排而不使用堆排序

最大的也是唯一的缺点就是——堆的维护问题,实际场景中的数据是频繁发生变动的,而对于待排序序列的每次更新(增,删,改),我们都要重新做一遍堆的维护,以保证其特性,这在大多数情况下都是没有必要的。(所以快排成为了实际应用中的老大,而堆排序只能在算法书里面顶着光环,当然这么说有些过分了,当数据更新不很频繁的时候,当然堆排序更好些

Final和finally和finalize

final 是一个修饰符,如果一个类被声明为 final 则其不能再派生出新的子类,所以一个类不能既被声明为 abstract 又被声明为 final 的;将变量或方法声明为 final 可以保证它们在使用中不被改变(对于对象变量来说其引用不可变,即不能再指向其他的对象,但是对象的值可变),被声明为 final 的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改,被声明为 final 的方法也同样只能使用不能重载。使用 final 关键字如果编译器能够在编译阶段确定某变量的值则编译器就会把该变量当做编译期常量来使用,如果需要在运行时确定(譬如方法调用)则编译器就不会优化相关代码;将类、方法、变量声明为 final 能够提高性能,这样 JVM 就有机会进行估计并进行优化;接口中的变量都是 public static final 的。

finally 用来在异常处理时提供块来执行任何清除操作,如果抛出一个异常,则相匹配的 catch 子句就会执行,然后控制就会进入 finally 块。

finalize 是一个方法名,Java 允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作,这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,它是在 Object 类中定义的,因此所有的类都继承了它,子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作,finalize() 方法在垃圾收集器删除对象之前对这个对象调用的。

Final

finally

1.什么时候用到finally

某些事物在进行异常处理之后,需要回复到以前的状态。此时需要finally来帮助实现

2.finally语句的执行 是在try / catch 中 return语句 执行之后,还是返回之前呢?

2.1:finally代码块是在try代码块中 return语句执行之后返回之前 执行的

2.2:finally代码块中的return语句 覆盖try代码块中的return语句

2.3:如果finally代码块,没有return语句对try代码块的return语句进行覆盖的话,那么原来的返回值会因为finally代码块中的修改,会导致返回值可能改变 也可能不变。

如果返回值类型是传值类型:则不会改变返回值 /

如果返回值是传址类型:会对返回值造成影响(比如在finally代码块中对map进行添加时,key相同时,会进行添加覆盖)

传址:对象 / 数组 传值:8大基本数据类型 及其 包装类,字符常量

2.4:try代码块中的语句在异常情况下 不会继续向下执行。

3.总结:

3.1:try语句没有被执行,也就是说根本没有执行到try语句。就返回了,那么finally是不会被执行的。

说明了,finally执行的前提条件是 try语句一定被执行到。

3.2:如果在try代码块中,执行了System.exit(0),那么就会终止jvm,finally里面的语句肯定不会被执行到。

3.3:如果finally代码块中有return语句,则直接覆盖try/catch 语句直接返回

静态变量**、静态代码块、构造代码块、构造函数的执行顺序**

无父类的情况:

类加载:

静态变量、成员变量加载,初始化为零。

执行顺序:

静态变量>静态代码块>主函数(静态方法)>成员变量>构造代码块>构造函数

http://blog.csdn.net/csdn9988680/article/details/78236196

有父类的情况:

Instance of

java 中的 instanceof 运算符用来在运行时指出对象是否是特定类的一个实例,通过返回一个布尔值来指出这个对象是否是这个特定类或者是它的子类的一个实例;用法为 result = object instanceof class,参数 result 布尔类型,object 为必选项的实例,class 为必选项的任意已定义的对象类,如果 object 是 class 的一个实例则 instanceof 运算符返回 true,如果 object 不是指定类的一个实例或者 object 是 null 则返回 false;

abstract 的方法是否可同时是 static、是否可同时是 native、是否可同时是 synchronized 的?为什么?

:都不可以,原因如下。

首先 abstract 是抽象的(指方法只有声明没有实现,实现要放入声明该类的子类中),static 是一种属于类而不属于对象的关键字,synchronized 是一种线程并发锁关键字,native 是本地方法,其与抽象方法类似,只有声明没有实现,但是它把具体实现移交给了本地系统的函数库。

对于 static 来说声明 abstract 的方法说明需要子类重写该方法,如果同时声明 static 和 abstract,用类名调用一个抽象方法是行不通的。

对于 native 来说这个东西本身就和 abstract 冲突,因为他们都是方法的声明,只是一个把方法实现移交给子类,另一个是移交给本地操作系统,如果同时出现就相当于即把实现移交给子类又把实现移交给本地操作系统,那到底谁来实现具体方法就是个问题了。

对于 synchronized 来说同步是需要有具体操作才能同步的,如果像 abstract 只有方法声明,则同步就不知道该同步什么了。

内部类

静态内部类是

1.定义在另一个类里面用 static 修饰 class 的类,

2.内部类不需要依赖于外部类(与类的静态成员属性类似)且无法使用其外部类的非 static 属性或方法(因为在没有外部类对象的情况下可以直接创建静态内部类的对象,如果允许访问外部类的非 static 属性或者方法就会产生矛盾)。

 

成员内部类

1.是没有用 static 修饰且定义在在外部类类体中的类,是最普通的内部类,可以看做是外部类的成员,可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员),而外部类无法直接访问成员内部类的成员和属性,要想访问必须得先创建一个成员内部类的对象然后通过指向这个对象的引用来访问;

2.当成员内部类拥有和外部类同名的成员变量或者方法时会发生隐藏现象(即默认情况下访问的是成员内部类的成员,如果要访问外部类的同名成员需要通过 OutClass.this.XXX 形式访问);成员内部类的 class 前面可以有 private 等修饰符存在。

 

方法内部类(局部内部类)

1.是定义在一个方法里面的类,和成员内部类的区别在于方法内部类的访问仅限于方法内;

2.方法内部类就像是方法里面的一个局部变量一样,所以其类 class 前面是不能有 public、protected、private、static 修饰符的,也不可以在此方法外对其实例化使用。

 

匿名内部类

1.是一种没有构造器的类(实质是继承类或实现接口的子类匿名对象),

2.由于没有构造器所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调,匿名内部类在编译的时候由系统自动起名为 OutClass$1.class,一般匿名内部类用于继承其他类或实现接口且不需要增加额外方法的场景(只是对继承方法的实现或是重写);匿名内部类的 class 前面不能有 pravite 等修饰符和 static 修饰符;匿名内部类访问外部类的成员属性时外部类的成员属性需要添加 final 修饰(1.8 开始可以不用)。

静态变量和**实例变量的区别**

\1. 实例变量必须创建了对象才能分配内存和使用,其属于对象;静态变量编译期分配内存,通过类名调用,其属于类

\2. 静态变量所有对象共享一个,实例变量每个对象都有

内部类的分类

在Java中,可以将一个类定义在另一个类里面或者一个方法里边,这样的类称为内部类,广泛意义上的内部类一般包括四种:成员内部类,局部内部类,匿名内部类,静态内部类 。

 

 

1.成员内部类

(1)该类像是外部类的一个成员,可以无条件的访问外部类的所有成员属性和成员方法(包括private成员和静态成员);

(2)成员内部类拥有与外部类同名的成员变量时,使用就近原则。如果要访问外部类中的成员,需要:【外部类.this.成员变量 或 外部类.this.成员方法】;

(3)在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问;

(4)成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象;

(5)内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。如果成员内部类用private修饰,则只能在外部类的内部访问;如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。外部类只能被public和包访问两种权限修饰。

2.局部内部类

(1)局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内;

(2)局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

3.匿名内部类

(1)一般使用匿名内部类的方法来编写事件监听代码

(2)匿名内部类是不能有访问修饰符和static修饰符的;

(3)匿名内部类是唯一一种没有构造器的类

(4)匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

匿名内部类的创建格式为: new 父类构造器(参数列表)|实现接口(){

//匿名内部类的类体实现

}

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。

3、匿名内部类中不能存在任何的静态成员变量和静态方法。

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

4.内部静态类

(1)静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似;

(2)不能使用外部类的非static成员变量或者方法。

外部类修饰符

  • public(访问控制符),将一个类声明为公共类,它可以被任何对象访问,一个程序的主类必须是公共类。

  • default(访问控制符),类只对包内可见,包外不可见。

  • abstract(非访问控制符),将一个类声明为抽象类,抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充,抽象类可以包含抽象方法和非抽象方法。。

  • final(非访问控制符),将一个类生命为最终(即非继承类),表示它不能被其他类继承。

注意:

1.protected 和 private 不能修饰外部类,是因为外部类放在包中,只有两种可能,包可见和包不可见。

  1. final 和 abstract不能同时修饰外部类,因为该类要么能被继承要么不能被继承,二者只能选其一。

3.不能用static修饰类,因为类加载后才会加载静态成员变量。所以不能用static修饰类和接口,因为类还没加载,无法使用static关键字。

内部类修饰符

内部类与成员变量地位一直,所以可以public,protected、default和private,同时还可以用static修饰,表示嵌套内部类,不用实例化外部类,即可调用。

方法修饰符

  1. public(公共控制符),包外包内都可以调用该方法。

  2. protected(保护访问控制符)指定该方法可以被它的类和子类进行访问。

  3. default(默认权限),指定该方法只对同包可见,对不同包(含不同包的子类)不可见。

  4. private(私有控制符)指定此方法只能有自己类等方法访问,其他的类不能访问(包括子类),非常严格的控制。

  5. final ,指定方法已完备,不能再进行继承扩充。

  6. static,指定不需要实例化就可以激活的一个方法,即在内存中只有一份,通过类名即可调用。

  7. synchronize,同步修饰符,在多个线程中,该修饰符用于在运行前,对它所属的方法加锁,以防止其他线程的访问,运行结束后解锁。

  8. native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。

  9. abstract ,抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成 final 和 static。 任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。 抽象方法的声明以分号结尾,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值