class OuterClass{
    . . .
   class NestedClass{
       . . .
   }
}
class OuterClass{
    . . .
    public void doSomething(){
        class LocalClass{
            . . .
      }
    }
}
Lambda表达式可以有一个嵌套类和方法内部类
这样做是为了让代码更清晰,这样对象就会是"技术对象",你不会想在其他地方提到这个东西,也不会用它。
匿名类 : 
class NamedClass implements Interface{
     . . . 
}
Interface anObject = new NamedClass(. . .) ;
有时候我们并不是真正需要一个对象,而只是需要其中一个方法。
Interface anObject = new Interface() { . . . }
但Java可以动态地创建一个接口,并在传递参数到参数列表的时候实例化一个对象。
继承可以有同样的效果 :
class NamedClass extends ParentClass{
    . . . 
} 
NamedClass anObject = new NamedClass( . . . ) ;
ParentClass anObject = new ParentClass(){//attribute and method definition};
很多例子都体现在图形接口(graphical interface)
Button btn = new Button() ;
btn.setText("Say Hi") ; 
btn.setOnAction(new
                       EventHandler<ActionEvent>(){
                    @Override
                    public void handle(ActionEvent e){
                          System.out.println("Hi!") ;
                    }
}) ;

如果你的接口只需要一个函数,Lambda函数就可以派上用场。
Lambda函数表达式来自Lambda微积分:
                                                     λx.(4x^3 + 2x + 1)
括号前面的标记说明了x是变量,括号内的内容就是函数表达式,通常标记为M。带入值E的写法:
                                              ( ( λx.M ) E ) -> ( M[ x:=E ] )

Lambda表达式只是一个符号,就像0一样,它也可以像0一样掀起一个领域的全新远景的打开。它使程序更容易阅读——这意味着更少的BUG。
Lambda表达式只在函数接口(Functional interface)中管用,函数接口是指只有一个抽象方法的接口 :
                                                @FunctionalInterface
写法 : 
                                               ( 参数列表 ) -> { 方法体 }
参数列表中的参数类型可以选填。因为只有一个方法,方法的名字都可以省掉。
前面的例子可以改为:
btn.setOnAction( (e)->{
                      System.out.println("Hi!") ;
                    }) ;

Lambda函数还可以用在容器中的查找,下面的例子会介绍:
class Film {
    private String title ;
    private String countries ;
    private int       year ;
    private float    billionRMB ;
    //构造器
    public Film(String title, String countries, int year, float billionRMB) { . . . }
    //get方法
    //toString()重写
    . . . . . .
}
假设一个Film类存储了一些票房信息。
从文件中提取数据到list :
       ArrayList<Film> films = new ArrayList<Film>() ;
现在的问题是如何检索这个容器。我们可以检索很多标准:电影名字,发行时间,国家等。
第一步:添加一些方法到Film类 :
public boolean selectByTitle(String str){ return this.title.contains(str) ; }
public boolean selectByCountry(String cntry){ return this.countries.contains(cntry) ; }
. . . . . .(体力劳动,继续添加,限制条件又很多,必须一条一条添加
第二步 :使用匿名类
interface SelectFilm { boolean test(Film film) ; }
static void showFilms(SelectFilm tester){
    for(Film f : films)
        if(tester.test(f)) System.out.println(f);
}
showFilms(new SelectFilm() { public boolean test(Film f){
                                                     return f.getYear() == 2014 ;
                                              }
                                        } ) ;
第三步:转为Lambda表达式
showFilms( (f) -> { return f.getYear() == 2014 } )
实际上,因为返回类型是直接计算的,可以更加简化:
showFilms( (f) -> f.getYear() == 2014 ) ;
第四步:多样的内部函数接口
由于需要测试的东西实在太多了,就需要一个可以判断多点东西的方法。
import java.util.function.Predicate ;
        Predicate<Film> pred
下面是一个使用Predicate接口的例子:
static boid filter(Predicate<Film> pred) {
    Film f ;
    ListIerator<Film> iter = films.listIterator() ;
    while (iter.hasNext()){
        f = iter.next() ;
        if(pred.test(f)) System.out.println(f) ;
    }
}
filter( (film) -> film.getYear() == 2014 ) ;
这里有几个可以用的函数式接口:
Supplier/Consumer与多线程有关,以后的课会讲。
Predicate<T>                   T -> boolean
Supplier<R>               void -> R
Consumer<T>                 T -> void
Function<T,R>                 T -> R
UnaryOperator<T>          T->T


Streams(流)
流和文件(InputStream, OutputStream)没有半毛钱关系,它是关于链式过程的编程方法
当你对String应用一个返回String的方法的时候,你可以继续应用新方法 :
String str = "now let's have some fun" ;
str. toUpperCase(). replace('S','D'). substring(15,19). replace('M','N')
有一种函数式编程(functional programming)的方法可以表示这一种过程
因为使用的函数都是返回String,我们可以将它们串起来。
同样的,容器类也可以应用这种方法。甚至SQL也要用这种思想。
流操作需要一个返回流的方法,且不总结流过程。
中间操作(Intermediate operations)
filter 
distinct
sorted
map
终结操作(Termial operations)
foreach
toArray
reduce
count
min, max
对于上面的例子,我们可以把一个容器转换为流:
ArrayList<Film> films = . . .
films.stream()
filims.stream().filter( (film)->film.getYear() == 2014 )
这种操作会对容器内的每一个元素进行。我们也可以详细地打印出这个过程:
films.stream().filter( (film)->film.getYear() == 2014 ).forEach(System.out : : println ) ;
forEach()是一个终结操作,println()方法应用在forEach()上。特别的符号" : : "指出这个方法应用在每个元素上。
如果Film类有comparable<T>接口,可以对其排序:
films.
stream().filter( (film)->film.getYear() == 2014 ).sorted().forEach(System.out : : println)

平行流(Parallel streams) : .parallelStream()
就像百川汇入大海,流也可以分成多个平行的分支,再达到相同的目标。我们会在以后讨论平行化(parallelism), 数据科学和大数据全是这种东西!


用户图形界面(Graphical User Interfaces, GUI)
你在实验室写的程序比你每天用的丑,丑上天际:在consoles里执行,从键盘读取,输出纯粹的文本 . . .八十年代,美丽的图形界面要花很多代码,其中的代码逻辑还和你平时写的不一样。
因为有很多图形包(Tons of graphcial packages),你不需要什么东西都自己写,你只需要躺着疯狂import。有一些低级的图形包可以生成线或曲线,一些高级的图形包可以生成按钮。
历史上java有几个图形包:1995年的AWT,1996年的Java Foundation Classes。在Java编程中,可以同时用几个包来建立GUI。Swing是依赖于AWT的,当写Swing的时候,就必须import AWT包,其他图像包也一样(javax.imageio.*)。
2008年,迎合安卓系统的发展,java有了JavaFX包。它也允许在程序外自定义修改界面,这个修改的文件叫作层叠样式表(Cascading Style Sheet, CSS),这是一种从Web编程借来的方法。
现在还有很多程序是用Swing编写的,同时学习JavaFX和Swing是个不错的选择。尽管它们的一些名词变了,但核心思想还是相近的。
JavaFX应用通常会根据一个固定结构设计:模型/界面/控制(Model/View/Controller,MVC),模型是为了管理数据,界面是为了用户友好,控制是为了操作逻辑更清晰。

不管用哪种语言,图形界面编程和算法编程有很大的区别,这种编程方法叫作事件驱动编程(Event-driven programming)
一个图形应用是一个大循环。在这个循环中,除了等待,你的程序什么都不需要做,等待什么?等待用户坐在电脑面前操作(除了挠头)。你的程序休眠并等待用户操作,当有用户来操作的时候,换句话来说,就是用户触发事件的时候,程序开始工作。
事件可以是任何事情,敲键盘,点鼠标,移动鼠标,点击屏幕,在网络摄像头前面跳起来...所有的这些都可以转换为电脑可以接收的电子信号。
你的图形应用在Windows管理器下运行也能够对事件发生反应。举个例子,鼠标可能会移动到程序的外面,但Windows管理器会获取鼠标在任何位置的坐标,这也是一个事件。这个事件会让不同的窗口的状态发生变化:鼠标指向一个新窗口,系统可以让这个窗口从休眠变成活跃,而刚才活跃的窗口变成休眠状态,利用这个状态可以调节窗口显示顺序。所有的这些都是Windows管理器做的,你不需要写这种代码。
你需要记住的是,图形界面是在Windows管理器的环境下的。

可视元素叫作窗口小部件(Window gadget, widget),如单选框,文本框,按钮,标签这些...还有一些不可视的部件,叫作容器(container)。容器的目的是创建一个有序布局(layout),这个布局可以让很多窗口小部件有序且有联系地展示在屏幕上。
如果你有一个尺寸不可变的窗口,事情会简单很多。当然,很多人会想到用变化比例来控制布局,但这件事情没那么简单。有一些容器的尺寸是固定的。
容器有一些可以解决这个问题的方法。两个盒子,横向盒(Horizontal boxes),和纵向盒(Vertical boxes)。
另一种就是网格(grids),网格允许按照行列关系坐标系来放置小部件,而不用距离坐标系。
关于容器最关键的就是它们是可嵌套的。你可以放一个纵向盒到横向盒里面,或放置一个网格,反之亦然。
例如,你可以放置两个纵向盒到一个横向盒里面。当窗口被调整尺寸的时候,全部局会按照盒的位置来调整。



做出反应(Callback) : 与事件有关的函数
最后一个很重要的概念就是处理器(handlers),这是一个与事件有关的函数。例如,单击一个按钮,会开始检索数据库。这个与按钮和数据库有关的行为就需要你来动手写。
预先定义事件(Predefined events):
destory window
button press/release
key press/release
focus in/out
move in/out
预先定义事件有很多(上面是一些例子),你只需要处理对你来说重要的事情就可以了,例如,窗口被关闭了,要弹出是否保存文本的提示框。


一个样例:移动鼠标到按钮时,按钮会移动到鼠标外的位置,使得鼠标无法点击按钮
一个JavaFX应用源自JavaFX的Application类,它是自动继承标准的抽象类和方法的。
创建一个Application类的示例:

使用init()函数:
init()
这个方法默认什么都不做,你可以重写这个方法,使它可以连接数据库,网络或者读取文件。
有一个必须重写的方法就是start()方法,这个方法把窗口小部件添加到窗口上,定义它们的外观和行为。
start( javafx.stage.Stage )
等待程序关闭的函数:
Platform.exit()
你必须且只需要写一个事件处理器。javaFx会运行程序知道它退出(可能与"退出"按钮有关,也看是检测到窗口被退出)。
使用stop()函数:
可以用stop()函数撤销你在init()函数做的操作:关闭数据库或网络。与init()一样,重写这个方法是可选的。


示例代码:
import java.util.Random; 
import javafx.application.Application; 
import javafx.event.ActionEvent;
import javafx.event.EventHandler; 
import javafx.scene.Group; 
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView; 
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage; 
import javafx.stage.Screen; 
import javafx.geometry.Rectangle2D;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
public class Soothsayerfx extends Application {
    private Rectangle2D screenBounds
                                 = Screen.getPrimary().getVisualBounds() ;
    private Random  rand_generator ;
    private double    x ;
    private double    y ;
    public static void main(String[] args){
         launch(args) ;  //launch(input)函数架设了与start()函数的桥梁,提供了一个窗口                                        //(stafe)
    }
    public void start (Stage stage){
        this.rand_generator = new Random() ;
        stage.setTitle("Meaning of Life") ;
        stage.setResizable(false) ;
        Group root = new Group() ;                               //创建一个名为root的Group
        Scene scene = new Scene(root) ;                       //创建一个Scene对象,传入root
        Scene.getStylesheets().add("soothsayer.css") ;  //添加风格修改文本
    }
}
如果没有CSS文件,console就会发出警告。
示例:
.root {
         -fx-font-size : 28 pt ;
}
可以使用java反射机制找到文件的位置 :
private String diretory = Soothsayerfx class.getProtectionDomain()
                                                                      .getCodeSource()
                                                                      .getLocation()
                                                                      .toString() ;
scene.getStylesheets().add(directory + "soothsayer.css" ) ;

接着上面的示例代码 : 
        Group root = new Group() ;                               //创建一个名为root的Group
        Scene scene = new Scene(root) ;                       //创建一个Scene对象,传入root
        Scene.getStylesheets().add("soothsayer.css") ;  //添加风格修改文本
        VBox vBox = new VBox() ;                                  //创建一个纵向盒
        vBox.setPadding(new Insets(10)) ;                      //
        vBox.setAlignment(Pos.CENTER) ;
        root.getChildren().add(vBox) ;
        Label label = 
              new Label("Click the button to have the meaning of life revealed") ;
        vBox.getChildren().add(label) ;
        Image myPicture = new Image("psychic.png") ;
        ImageView img = new ImageView() ;
        img.setImage(myPicture) ;
        vBox.getChildren.add(img) ;
        Button button = new Button("Click to Learn") ;
        vBox.getChildren().add(button) ;
        button.addEventHandler(MouseEvent.MOUSE_ENTERED, 
                  new EventHandler<MouseEvent>(){
                          @Override
                          public void handle(MouseEvent e){
                                   moveWindow(stage) ;
                          }
                    }) ;
        stage.setScene(scene) ;
        stage.show() ;
        stage.setX( (screenBounds.getWidth() - stage.getWidth() )/ 2  ) ;
        stage.setY( (screenBounds.getHeight() - stage.getHeight() )/ 2  ) ;
        this.x = stage.getX() ;
        this.y = stage.getY() ;
    }
    private void moveWindow(Stage stage){
        double height = screenBounds.getHeight() ;
        double width = screenBounds.getWidth() ;
        double x_move = width / 10 + rand_generator.nextDouble() * width / 2 ;
        double y_move = height / 10 +rand_generator.nextDouble() * height / 2 ;
        this.x = (double)((long)(this.x + x_move) % (long)(width - stage.getWidth())) ;
        this.y = (double)((long)(this.x + x_move) % (long)(height - stage.getHeight())) ;
        stage.setX(this.x) ; 
        stage.setY(this.y) ;
    }
}
整个代码没有明确的测试和循环,只有事件。
你可以自定义自己的窗口小部件。可以使用工具创建XML(FXML)文件去描述窗口小部件和容器。JavaFx会装载这个部件,部件的static方法会创建这个对象。
有很多个布局生成器,有一个比较特殊的是Scene Builder
有了Scene Builder,你可以把窗口小部件来拖去,放在自己想要的位置。设计完毕后保存为.fxml文件。
<?xml version="1.0" encoding="UTF-8"?> 
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> 
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> 
<vBox alignment="CENTER" maxHeight="-Infinity" maxWidth="Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="8.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http:// javafx.com/fxml/1" fx:controller="Soothsayerfxml"> 

你必须创建一个FXML加载器对象,这个对象的构造函数会抛出异常。因为代码被分成不同的模块,所以行为必须是public的,一些属性必须是static的。
生成代码 -> Soothsayerfxml控制器


事件和监听器(Events and Change Listeners)
事件和监听器的关系就像exceptions和catch,以这种模式来定义它们。
有很多诸如此类的代码 :
.setOnSomeAction () 
KeyPressed, KeyReleased, KeyTyped, MouseClicked, MouseExited

.setOnAction() 方法是给Buttons, Radio Buttons, Check Boxes的
setOnAction()里面要有一个处理器接口,这就可以使用Lambda表达式了 :
Button btn = new Button() ;
btn.setText("Say Hi") ;
btn.setOnAction( (e) -> {    System.out.println("Hi!") ;   } ) ;
另一种在示例代码中的方法就是 :
button.addEventHandle(MouseEvent.MOUSE_ENTERED, (e)->{moveWindow(stage);});