java内部类技术提炼
时间:2016.07.28,2016.07.29
参考书籍:《Thinking in Java》、《Effective Java》
备注:这篇文章并不打算介绍内部类相关的一切技术细节,在《Thinking in Java》里已经介绍的很详细了,我只想重点谈谈一些我所了解的内部类的应用。顺便提纲挈领的对《Thinking in Java》相应章节作一定总结。
一些提炼:
一.真正的内部类
1.内部类拥有其外部类的所有元素(方法和字段)的访问权(原理在于Java默认做到了:当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。)
2.内部类的创建:
(1)首先,最直接创建内部类的地方只能是在外部类的方法里。
(2)外部类可以写一个工厂方法来返回创建的内部类。
一种形式是这样:OuterClass.InnerClass innerClass = outerClass.getInner();
package cn.j; /**
* @ClassName: OutClass
* @Description: thinking in java
* @author 无名
* @date 2016-7-28 下午9:37:59
* @version 1.0
*/
public class OutClass {
private int outValue = 1; InnerClass getInner() {
return new InnerClass();
} public void outSay(){
System.out.println("from out function.");
} class InnerClass {
public void innerSay() {
System.out.println("I am inner,and out value = " + outValue);
outSay();
}
} public static void main(String[] args) {
OutClass outClass = new OutClass();
OutClass.InnerClass innerClass = outClass.getInner();
innerClass.innerSay();
}
}
(3)如果内部类实现了某个接口Inter,则可以 Inter innerClass = outerClass.getInner();
这便是内部类的向上造型
package cn.j;
interface Inter{
public void innerSay();
}
/**
* @ClassName: OutClass
* @Description: thinking in java
* @author 无名
* @date 2016-7-28 下午9:37:59
* @version 1.0
*/
public class OutClass {
private int outValue = 1;
InnerClass getInner() {
return new InnerClass();
}
public void outSay(){
System.out.println("from out function.");
}
class InnerClass implements Inter{
@Override
public void innerSay() {
System.out.println("I am inner,and out value = " + outValue);
outSay();
}
}
public static void main(String[] args) {
OutClass outClass = new OutClass();
Inter innerClass = outClass.getInner();
innerClass.innerSay();
}
}
3.在方法内的内部类,局部内部类:
局部内部类与一般内部类有很多相似之处,在于其对外部类的访问权限上。不同之处,在于对于局部内部类的创建,只能在包含该类的方法之上。
package cn.j;
public class TestLocalInnerClass {
public int int00 = 1;
public void outFunc(){
System.out.println("I am Outter class");
}
public void func1(){
class InnerLocalClass{
public void say(){
System.out.println("I am InnerLocalClass value:" + int00);
outFunc();
}
}
InnerLocalClass ilc = new InnerLocalClass();
ilc.say();
}
public static void main(String[] args){
TestLocalInnerClass tlic = new TestLocalInnerClass();
tlic.func1();
}
}
4.匿名内部类:
要我来做匿名内部类的示例的话,非常简单:
package cn.j;
/**
* @ClassName: MyAnonymousInterface
* @Description: Thinking in Java
* @author 无名
* @date 2016-7-29 下午8:26:52
* @version 1.0
*/
class MyInnerClass {
private String msg = "A";
public void say(){
System.out.println("I am MyInnerClass" + msg);
}
}
public class AnonymousClassTest {
public static void main(String[] args){
new MyInnerClass().say();
}
}
package cn.j;
/**
* @ClassName: MyAnonymousInterface
* @Description: Thinking in Java
* @author 无名
* @date 2016-7-29 下午8:26:52
* @version 1.0
*/
interface MyAnonymousInterface{
public void say();
}
public class AnonymousClassTest {
public static void main(String[] args){
new MyAnonymousInterface(){
private String msg = "A";
public void say(){
System.out.println("I am MyAnonymousInterface" + msg);
}
}.say();
}
}
先看看第一个示例,再看看第二个示例,很容易看出其中玄机。匿名内部类其实都是,通过new表达式来直接实现一个接口。在这个过程中插入一个类的定义后立刻便new出这个类并使用它。
之前看到类似这样的创建线程的写法,当时还觉得很nb,其实如果熟悉匿名内部类了,再看真的很easy。这种写法可能就是为了装b吧。具体原理在于Thread的创建可以是以一个实现了Runnable接口的类作为参数的,而这个实现了Runnable接口的类在这里以匿名内部类来实现。
总体来讲,配合上面示例1,然后示例2,再加上对Thread创建方式的理解,便可以完全了解下面的代码:
new Thread(new Runnable()
{
public void run()
{
while (true)
{
System.out.println("fu波多野结衣ck");
}
}
}).start();
有一点要补充一 下:局部内部类和匿名内部类能且只能访问局部final变量:
类似这样的效果:
package cn.j;
public class TestLocalInnerClass {
public void func1(){
final int localV = 3;
class InnerLocalClass{
public void say(){
System.out.println("I am InnerLocalClass value:" + localV);
}
}
InnerLocalClass ilc = new InnerLocalClass();
ilc.say();
}
public static void main(String[] args){
TestLocalInnerClass tlic = new TestLocalInnerClass();
tlic.func1();
}
}
5.嵌套类(static修饰的内部类):
(1)You don't need an outer-class object in order to create an object of a nested class.
(2)You can't access a non-static outer-class object from an object of a nested class.
看不懂Thinking in java的英文,看下面的代码也懂了。
package cn.j; /**
* @ClassName: NestedClassTest
* @Description: Thinking in Java
* @author 无名
* @date 2016-7-29 下午9:33:28
* @version 1.0
*/
public class NestedClassTest {
private int outV00 = 1;
public static int outV01 = 1;
public void outSay00(){
System.out.println("out say");
}
public static void outSay01(){
System.out.println("out static say");
}
public static class NestedC00 {
private int intV00;
public void innerFunc(){
intV00 = outV01;
outSay01();
}
}
public static void main(String[] args){
NestedC00 nc00 = new NestedC00();
nc00.innerFunc();
}
}
6.接口中的类
接口中的类类似于嵌套类的情况,本身和接口没有本质联系(默认public static),只是处于其命名空间之下
package cn.j;
import cn.j.ClassInInterface.Test;
interface ClassInInterface {
void func00();
class Test implements ClassInInterface {
@Override
public void func00() {
System.out.println("Howdy");
}
}
}
/**
* @ClassName: ClassInInterfaceTest
* @Description: Thinking in Java
* @author 无名
* @date 2016-7-29 下午9:45:08
* @version 1.0
*/
public class ClassInInterfaceTest {
public static void main(String[] args) {
Test test = new Test();
test.func00();
}
}
我所知道的内部类的几个应用:
1.《Effective Java》第二章第2条,讲到使用嵌套类完成构建器设计模式。
package cn.j;
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbonhydrate;
public static class Builder {
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbonhydrate = 0;
public Builder(int servingSize,int serving){
this.servingSize = servingSize;
this.servings = serving;
}
public Builder calories(int val){
this.calories = val;
return this;
}
public Builder fat(int val){
this.fat = val;
return this;
}
public Builder carbonhyate(int val){
this.carbonhydrate = val;
return this;
}
public Builder sodium(int val){
this.sodium = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbonhydrate = builder.carbonhydrate;
}
public static void main(String[] args) {
NutritionFacts cocacola = new NutritionFacts.Builder(240,80).calories(100).sodium(35).carbonhyate(27).build();
}
}
结合代码来看,如果单纯用构造函数来初始化那些参数的话,会比较麻烦,而难以阅读,使用易于出错。
例如new Test(1,2,3,4,5,6,7,8,9);这种写法。
上述代码的形式,NutritionFacts cocacola = new NutritionFacts.Builder(240,80).calories(100).sodium(35).carbonhyate(27).build();可读性非常强。builder模式模拟了具名的可选参数。
2. 项目中遇到的,自定义注解实现前后台参数校验,用到了内部类:
package sonn.sonnannotation; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload; import sonn.util.StringUtill; /**
* @ClassName: IsValidString
* @Description: 自定义注解实现前后台参数校验,判断是否包含非法字符
* @author 无名
* @date 2016-7-25 下午8:22:58
* @version 1.0
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IsValidString.ValidStringChecker.class)
@Documented
public @interface IsValidString
{
String message() default "The string is invalid."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default{}; class ValidStringChecker implements ConstraintValidator<IsValidString,String>
{ @Override
public void initialize(IsValidString arg0)
{
} @Override
public boolean isValid(String strValue, ConstraintValidatorContext context)
{
if(StringUtill.isStringEmpty(strValue))
{
return true;
}
if(strValue.contains("<"))
{
return false;
}
return true;
} }
}
具体内容可以参考我的博文:http://blog.csdn.net/sonnadolf/article/details/52040017
3.之前提到的,匿名内部类创建线程的写法。其实很多时候都可以采用匿名内部类方式实现接口,直接new一个类来使用。只是大家不熟悉这个用法罢了。
spring源码实例化singleton bean的代码:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
4.java jdk的线程池的Executors类,用到了嵌套类。
类似这样:
public class Executors {
..........
static class DefaultThreadFactory implements ThreadFactory {
..........
}
..........
}
5.非常非常重要的一点,内部类是tmd可以弥补java无法多继承缺点的啊。虽然我觉得有点扯淡。因为java本身不支持多继承。用内部类不知算什么……
java内部类技术提炼的更多相关文章
- 深入分析Java Web技术内幕(修订版)
阿里巴巴集团技术丛书 深入分析Java Web技术内幕(修订版)(阿里巴巴集团技术丛书.技术大牛范禹.玉伯.毕玄联合力荐!大型互联网公司开发应用实践!) 许令波 著 ISBN 978-7-121- ...
- java 反射技术
什么是反射?反射就是将字节码中的各种成分映射到相应的java类中来,java反射技术自JDK1.1以来就出现了,目前大多数流行的框架都采用了这种技术,可见其重要性,这篇文章将详细介绍我对java反射技 ...
- java 内部类(摘抄自网络)
Java内部类 1.内部类分为成员内部类.静态嵌套类.方法内部类.匿名内部类. 几种内部类的共性: A.内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类 ...
- (转)Java动态追踪技术探究
背景:美团的技术沙龙分享的文章都还是很不错的,通俗易懂,开阔视野,后面又机会要好好实践一番. Java动态追踪技术探究 楔子 jsp的修改 重新加载不需要重启servlet.如何在不重启jvm的情况下 ...
- 如何才能够系统地学习Java并发技术?
微信公众号[Java技术江湖]一位阿里Java工程师的技术小站 Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容. 这里不仅仅是指使用简单的多线程编程,或者使用juc的某个类.当然这些 ...
- Java动态追踪技术探究
引子 在遥远的希艾斯星球爪哇国塞沃城中,两名年轻的程序员正在为一件事情苦恼,程序出问题了,一时看不出问题出在哪里,于是有了以下对话: “Debug一下吧.” “线上机器,没开Debug端口.” “看日 ...
- Java内部类详解(一)
(转自:http://blog.csdn.net/wangpeng047/article/details/12344593) 很多人对于Java内部类(Inner Class)都十分陌生,甚至听都没听 ...
- Java内部类超详细总结(含代码示例)
什么是内部类 什么是内部类? 顾名思义,就是将一个类的定义放在另一个类的内部. 概念很清楚,感觉很简单,其实关键在于这个内部类放置的位置,可以是一个类的作用域范围.一个方法的或是一个代码块的作用域范围 ...
- 夯实Java基础系列18:深入理解Java内部类及其实现原理
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
随机推荐
- 【Mail】JavaMail介绍及发送邮件(一)
JavaMail介绍 JavaMail是SUN提供给开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支持常用的邮件协议,如SMTP.POP3.IMAP,开发人员使用JavaMail ...
- text-shadow文字阴影属性用法
text-shadow:offset-x:阴影水平移动,负值时向左偏移 text-shadow:offset-y:阴影垂直移动,负值时向上移动 text-shadow:radio-bluer:阴影到实 ...
- Oracle数据导入导出
Oracle数据导入导出imp/exp 在oracle安装目录下有EXP.EXE与IMP.EXE这2个文件,他们分别被用来执行数据库的导入导出.所以Oracle数据导入导出imp/exp就相当与ora ...
- 爱上WPF,努力才会有希望!
从WinForm转向WPF开发已经有两个多月了,通过不断深入地学习与运用,现在是越来越爱它了.它实在是太强大了.运用WPF,你不仅可以做Win界面,也可以很快转向Web开发,因为Silverlight ...
- using 语句中使用的类型必须可隐式转换为“System.IDisposable
在使用 EF 出现 using 语句中使用的类型必须可隐式转换为“System.IDisposable 今天写在这里分享给大家 出现这样的问题,是因为没有引用 EntityFramework 这个程 ...
- 19. UIAlertController 提示框获取文本内容,打印控制台上
1.首先定义一个全局字符串变量,方便接收获取的文本内容 2. -(void)viewDidAppear:(BOOL)animated{ UIAlertController * alert = [UIA ...
- Fiddler 工作原理
Fiddler工作原理: 就在在客户端与服务器端创建一个代理服务器: 在开启Fiddler后,Fiddler会自动窜改浏览器的代理,例如我们打开Fiddler,打开IE浏览器--设置--Interne ...
- 关于L'Hopital法则
1.首先需要使用 罗尔定理 函数f(x)在闭区间[a,b]连续在开区间(a,b)可微,如果f(a)=f(b),那么至少存在一点c使函数导数f'(c)=0 注意需要再(a,b)可微,如果函数有角点,断点 ...
- linux进程间通信-共享内存
转载:http://www.cnblogs.com/fangshenghui/p/4039720.html 一 共享内存介绍 共享内存可以从字面上去理解,就把一片逻辑内存共享出来,让不同的进程去访问它 ...
- ruby 学习笔记 2 -变量
变量 在ruby的世界里,变量有5种,全局变量 局部变量 实例变量 常量 类变量以及伪变量 常用的: 全局: 在全局使用,使用$开头,因为是全局的,所以在任何的代码例子中都可以改变其值,造成混乱,所以 ...