JavaBaseNote
[TOC]
对象
- 万物皆对象
按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类’就是‘类型’的同义词。
——《JAVA编程思想》
简而言之,对象是类的实例。比如所有的电脑叫“电脑”,电脑是一个类,而具体下来,你的mac是一个对象,是“电脑”的实例。
对象引用
举例:
1 | Person person = new Person("大大卷"); |
- 在堆空间里创建一个Person类对象
- 末尾的()意味着在对象创建后,立即调用Person类的构造函数,对刚生成的对象进行初始化。
- 等号左边,其实是创建了一个Person类引用变量person,用来指向Person对象的对象引用。
- “=”操作符使对象引用指向刚创建的Person对象
1 | Person person; |
两段代码功能一样。
两个实体:对象引用变量和对象本身。
一个Person类可以创建出无数个对象,但这些对象不可能全不都叫“Peron”叭 _(:з」∠) _
对象连名都没有,没法直接访问它。我们只能通过对象引用来间接访问对象。
Java中都是通过引用来操纵对象的。
Java对象和引用的关系可以说是互相关联,却又彼此独立。彼此独立主要表现在:引用是可以改变的,它可以指向别的对象。
从存储空间上来说,对象和引用也是独立的,它们存储在不同的地方,对象一般存储在堆中,而引用存储在速度更快的堆栈中。
对象好比是一只很大的气球,引用变量则是一根可以用来系气球的绳子。
如果只执行了第一条语句,还没执行第二条,此时创建的引用变量person还没指向任何一个对象,它的值是null。
引用变量可以指向某个对象,或者为null。
它是一根绳,一根还没有系上任何一个汽球的绳。执行了第二句后,一只新汽球做出来了,并被系在person这根绳上。抓住这根绳,就等于抓住了那只汽球。
每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。
也就是说person是一个引用,是指向一个可以指向Person类的对象的引用。真正创建对象的语句是右边的new Person(“大大卷”);
事实上到这里还会有点懵,但再看下面的例子就会比较清晰了:
1 | Person person; |
先让person指向了大大卷这个对象,然后又指向了毛毛虫这个对象。da ga la!!Person person只是生命了一个Person类的引用,它可以指向任何Person类的实例。类似于:
1 | int a; |
哇塞这个就很清楚啦=-=先声明一个int类型的变量a,对a赋值为2,然后又赋值为3.
如果来一句
1 | Person person2; |
其实发生的是复制行为。但是对象本身没有被复制,被复制的知识对象引用。person2也指向了person所指向的对象。两根绳系的是同一只气球哦!!!
然后,如果
1 | person2 = new Person(); |
那么person也指向了第二个对象,而第一个对象呢?飞走啦,据说是被JAVA的垃圾回收机制回收了,emmmmmm好吧 这很可以
and如果仅仅是为了打印而生成一个对象,就不需要用引用变量来系住它。例如打印字符串:
1 | System.out.println(“I”); |
字符串对象“I”在打印后即被丢弃。有人把这种对象称之为临时对象。
对象与引用的关系将持续到对象回收。
完了对象回收又是咋回事啊/笑哭
总之,
在初始化时,“=”语句左边的是引用,右边new出来的是对象。在后面的左右都是引用的“=”语句时,左右的引用同时指向了右边引用所指向的对象
参考:
为什么要接口
抽象类:每个类只能扩展于一个类,不能扩展第二个类了
but每个类可以实现多个接口
避免多重继承的复杂性和低效性。
break||continue||return
break 用于完全结束一个循环,跳出循环体。不管哪种循环,一旦在循环体中遇到break,系统将完全结束掉该循环。
continue只是忽略本次循环剩下的语句,立即开始下次循环,并不会终止循环;而break则是完全终止循环。
return不是专门拿来结束循环的,return的功能是结束一个 方法 。当一个方法执行到一个return语句时,这个方法将会给结束。
方法
封装了由一系列语句所描述的运算
need 参数(parameter) 算出某种数据类型的返回值
性质
- 方法的参数按值传递
不能改变原数组变量本身
- 方法名可以被重载
- 方法只能返回一个值
but可以有多个return语句 - 方法可以产生副作用
void 类型的静态方法会产生副作用(接受输入、产生输出、修改数组或者改变系统状态)
静态方法&非静态方法(实例方法)
又称函数——一组在被调用时会被顺序执行的语句.
made of 签名(关键字public static以及函数的返回值,方法名以及一串各种类型的参数)和函数体(即{code})
我终于在今天(24号,晚到惭愧)体会到 这其实跟数学里的函数意思是差不多的,就像一个计算过程一样
调用函数只需要:方法名(参数)
封装
对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。
1 | 使用封装有三大好处: |
继承
构造器
- 子类可以继承父类的属性和方法,但不能继承构造器,只能调用父类的构造器
使用super()
protected关键字
向上转型
多态
一个对象变量可以指示多种实际类型的现象被称为多态
在运行时能够自动地选择调用哪个方法的现象称为动态绑定
反射
指能够分析类能力的程序。
反射库大量应用于JavaBeans中
- 在运行时分析类的能力
检查类的结构
- 在运行时查看对象
- 实现通用的数组操纵代码
- 利用method对象(类比于c++函数指针
try{可能抛出已检查异常的方法调用代码}
catch{处理器代码}
接口
接口不是类,而是对类的一组需求描述(可以看成是没有实例域的抽象类
1)将类声明为实现给定的接口
2)对接口中所有方法进行定义
要将类声明为实现某个接口,需要使用implements
1.所有方法自动属于public;接口中的域将被自动设为public static final
2.接口不是类,尤其不能用new运算符实例化一个接口;
1 | x = new Comparable() // ERROR |
尽管不能构造接口的对象,却能声明接口的变量
1 | Comparable x; // Ok |
接口变量必须引用实现了接口的类对象;
1 | x = new Employee() // ok provide Employee implements Comparable |
接口可以被拓展
1 | public interface Moveable |
虽然在接口中不能包含实例域或静态方法,但却可以包含常量
回调是一种常见的程序设计模式
1 | package com.company; |
lambda表达式
Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码
lambda表达式就是一个代码块,以及必须传入代码的变量规范
即使lambda表达式没有参数,仍然要提供空括号,就像无参数方法一样
1 | () -> { for (int i = 100; i>=0; i--) System.out.println(i);} |
lambda表达式:
- 一个代码块
- 参数
- 自有变量的值 指非参数而且不在代码中定义的变量
- (闭包)
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口(必须有一个抽象方法)
最好把lambda表达式看做是一个函数,而不是一个对象,接受lambda表达式可以传递到函数式接口
lambda表达式转换接口:
1 | Timer t = new Timer(1000, event -> |
方法引用
(现有的方法可以完成想要传递到其他代码的某个动作)
1 | Timer t = new Timer(1000, event -> System.out.println(event)); |
等于
1 | Timer t = new Timer(1000,System.out::println); |
方法引用:
1 | System.out::println |
等价于lambda表达式
1 | x -> System.out.println(x) |
要用::操作符分隔方法名与对象或类名
- object::instanceMethod
- Class::staticMethod
方法引用等价于提供方法参数的lambda表达式
- Class::instanceMethod
第一个参数会成为方法的目标
类似于lambda表达式,方法引用不能独立存在,总是会转换为函数式接口的实例。
构造器引用
1 | ArrayList<String> names = ...; |
变量作用域
lambda表达式可以捕获外围作用域中变量的值,但能引用值不会改变的变量
在一个lambda表达式中使用this关键字时,是指创建这个lambda表达式的方法的this参数
1 | public class Application() |
处理
延迟执行
- 在一个单独的线程中运行代码
- 多次运行代码
- 在算法的适当位置(例如,排序中的比较操作
- 发生某种情况
- 必要时
内部类
定义在另一个类中的类
why we need:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来
- 想要定义一个回调函数:匿名内部类
内部类
- 所有静态域都必须是final
因为我们希望一个静态域只有一个实例,不过对于每个外部对象,会分别有一个单独的内部类实例。如果这个域不是final,他可能就不是唯一的。
- 不能有static方法
使用内部类访问对象状态
1 | class TalkingClock |
TimePrinter类位于TalkingClock类内部,这并不意味着每个TalkingClock都有一个TimePrinter实例域,TimePrinter对象是由TalkingClock类的方法构造
TimePrinter类没有实例域或者名为beep的变量,beep引用了创建TimePrinter的TalkingClock对象的域
一个方法可以引用调用这个方法的对象数据域,内部类既可以访问自身的数据域,也可以访问创建他的外围对象的数据域
内部类的对象总有一个隐式引用,它指向了创建他的外部类对象,这种引用是不可见的!!
局部内部类
1 | public void start() |
- 不能用public/private声明
作用域被限定在声明这个局部类的块中,对外部世界完全隐藏
即使TalkingClock类中的其他代码也不能访问它,除start方法外,没有任何方法知道TimePrinter类的存在了哦!!!
- 不仅能够访问包含它们的外部类,还可以访问局部变量
1 | public void start(int interval, boolean beep) |
匿名内部类
假如只创建这个类的一个对象,就不必命名了
1 | public void start(int interval, boolean beep) |
————创建一个实现ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在{}内
1 | new SuperType(construction parameters) |
SuperType可以是接口(ActionListener),内部类需要实现这个接口;
SuperType可以是一个类,内部类就要扩展他
由于构造器名字必须与类名相同,而匿名类没有类名,所以匿名类无构造器。只有将构造器参数传递给超类构造器。
内部类实现接口的时候,不能有任何构造参数!!!
1 | new InterfaceType() |
如果构造参数的闭小括号后面跟一个开大括号,正在定义的就是匿名内部类
1 | package annoymousInnerClass; |
静态内部类
对象中不需要引用任何其他的对象,不需要访问外围类对象的时候 为此可以将这个内部类声明为static
只有内部类能够声明为static。静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有内部类完全一样。
可以有静态域和方法
代理
利用代理可以在运行时创建一个实现了一组给定接口的新类