这里坑你是原来看的版本实在是太老了,这里就应该是说的多态
对于面向对象的程序设计语言,多态是第三种最基本的特征(前两种是数据抽象和继承)。
面向对象的三个基本特征是,封装,继承,和多态。这样封装可以理解成数据的抽象,多态指的就是这里的多形性
多形性从另一个角度将接口从具体的实施细节中分离出来,亦即实现了‘是什么’与‘怎么做’两个模块的分离。
这里这句话支出接口的作用那就是说明 ‘是什么’规定实现这个接口的实现类是什么作用,是什么类型的。然后再由这些实现类来具体做“怎么做”
多形性的问题也叫作动态绑定,推迟绑定,或者运行期绑定。
在第6章,大家已知道可将一个对象作为它自己的类型使用,或者作为它的基础类型的一个对象使用。取得一个对象句柄,并将其作为基础类型句柄使用的行为就叫作“上溯造型”
上溯造型 就是将一个对象句柄作为他的基础类型的句柄使用。
*如果我们能不顾衍生类,只让自己的代码与基础类打交道,那
么省下的工作量将是难以估计的。*
上溯造型和多形性也都是基于上面的理论来的,使用句柄时应该使用基础类的句柄,但具体实现的时候应该使用那些带有具体实现,功能强大的衍生类。
将一个方法调用通一个方法主体连接到一起称为“绑定”。
在程序运行以前执行绑定,叫“早期绑定”。它在任何程序化语言里都是不可能实现的,c编辑器只有一种方法调用,那就是‘早期绑定’
后期绑定,意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫做动态绑定,运行期绑定
java中绑定的所有方法都是采用后期绑定技术,除非一个方法已被声明为final。
java中private会自动加上final,final采用的不是后期绑定,应该是直接将代码的副本复制到调用的位置。
为什么要声明成final?防止别人覆盖那个方法,它可以有效的关闭动态绑定,这样一来,编译器可为final方法调用生成效率更高的代码。
如果是private修饰的代码应该是可以有更好的效率。
过载是指同一样东西在不同的地方具有多种含义,而“覆盖”是指它随时随地都只有一种含义,只是原有的含义被后来的含义取代了。
接口和内部类为我们提供了一种将接口和实现分离的更加结构化的方法。
抽象类是普通类和接口之间的一种中庸之道,尽管在构建具有某些未实现方法的类时,第一时间想到的应该是借口。
之所以要建立通用接口,唯一的原因就是他能为不同的子类型做出不同的表示。
包含了抽象方法的一个类叫作抽象类,如果一个类里包含了一个或多个抽象方法,类就必须制定成abstract(抽象)。
能够生成对象的类,必须是所有的方法都有明确的实现。
interface(接口),他可以称为更纯的抽象类,它允许创建者规定一个类的基本形式、方法名、自变量列表、以及返回类型,但是不规定方法主体。接口也包含了基本数据类型的数据成员,但他们都默认为static和final。接口只是提供一种形式,并不提供实施细节。
interface中数据必须先初始化,他默认的就是static和final
由于接口根本没有具体的实施细节,也就是说,没有与储存空间与接口关联在一起,所以没有任何办法可以防止多个接口合并到一起。
就是可以实现多继承。一个类可以同时实现多个类。
可以将一个类定义置入另一个类定义中。这就叫作内部类。内部类和合成方法存在根本的区别
在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,请尽量避免这种写法。(重载和重写混在一起的时候就会出现一些让人无语的错误)
有时这种写法会出现错误。
内部类对我们非常有用,因为利用它可对那些逻辑上相互联系的类进行分组,并可控制一个类在另一个类里的可见性。然而,我们必须认识到内部类与以前讲述的合成方法存在着更本的区别
public class InnerClassTest {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
}
public Contents cont() {
return new Contents();
}
public void ship(String dest) {
Contents c = cont();
}
public static void main(String[] args) {
InnerClassTest inn = new InnerClassTest();
InnerClassTest.Contents conten = inn.cont();
}
}
若想在除外部类非static方法内部之外的任何地方生成内部类的一个对象,必须将那个对象的类型设为“外部类名.内部类名”
在本类的非static方法内部生成内部类的对象可以直接new,但是在出本类外或者是本类的static的内部生成都必须使用外部类.内部类,而且还不能直接new,必须通过外部类的某个非static方法返回内部类的对象
package com.tang.stu;
//: Parcel3.java
//Returning a handle to an inner class
abstract class Contents {
abstract public int value();
}
interface Destination {
String readLabel();
}
public class Parcel3 {
private class PContents extends Contents {
private int i = 11;
public int value() {
return i;
}
}
protected class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new PDestination(s);
}
public Contents cont() {
return new PContents();
}
}
class Test {
public static void main(String[] args) {
Parcel3 p = new Parcel3();
Contents c = p.cont();
Destination d = p.dest("Tanzania");
// Illegal -- can't access private class:
// ! Parcel3.PContents c = p.new PContents();
}
} /// :~
当我们准备上溯造型(向上转型)到一个基础类(特别是到一个接口) 的时候,内部类就开始发挥其关键作用(从用于实现的对象生成一个接口句柄具有与上溯造型至一个基础类相同的效果) 。这是由于内部类随后可完全进入不可见或不可用状态——对任何人都将如此。所以我们可以非常方便地隐藏实施细节。我们得到的全部回报就是一个基础类或者接口的句柄,而且甚至有可能不知道准确的类型。
用内部类来封装一些不想让外界知道的细节的类,里面封装一些自己的功能。具体就是实现是,在外类中的方法中返回上溯造型中的基础类(可以是类,抽象类,接口),然后用自己的内部类继承或实现基础类,然后在外类的方法中返回内部类,因为内部类对外面(这里的内部类声明成,protected所以对外类的子类是没办法隐藏的)是隐藏的所以客户程序员不会知道怎么实现的,实现了对细节的隐藏。 这部分需要自己重新理解一下
创建内部类的动机
(1) 正如前面展示的那样,我们准备实现某种形式的接口,使自己能创建和返回一个句柄。
(2) 解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开。
// 这外面是有一个外部类,只是没有复制出来
public void tests() {
class Ress2{
public void sya() {
System.out.println("hi");
}
}
}
class Ress2{
public void sya() {
System.out.println("hi");
}
}
public void ship(String dest) {
Contents c = cont();
if(true) {
class Restt{
public void sya() {
System.out.println("--sya hi--");
}
}
Restt re = new Restt();
}
//在这里无法访问Restt
}
如果将一个内部类置于一个方法内部eg:tests()中一样,那样在方法以外的世界将无法访问到这个类,这个类只能够在在这个方法内部存在。
在ship()方法中将内部类置于if中那么在if的域外是无法访问Restt的,这里是有条件的,但是不意味着这里编译内部类是有条件编译的,他会随其他所有的东西一起编译。
匿名内部类
//这里的Cntetns是一个接口
public class Parcel6 {
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() {
return i;
}
}; // Semicolon required in this case
}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
Contents c = p.cont();
}
}
“创建从Contents衍生出来的匿名类的一个对象”。由new表达式返回的句柄会自动上溯造型成一个Contents句柄。
这里创建“Contents对象”是先衍生一个匿名类,然后上溯造型形成回到基础类Contents上。
//int i 是外类的属性 Ress2是一个内部类
private int i=0;
class Ress2 {
private int i2=2;
// private int i=2
public void sya() {
System.out.println("---->"+i);
}
}
java内部类是可以访问外类的属性的包括private属性,但是如果外类的属性名和自己的属性名重合,是输入自己属性名,外类的属性名就没办法调用了。
static内部类
如果不需要内部对象与其外围对类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类。
普通内部类对象隐式地保存了一个引用,它指向了外围类对象,然而当内部类是static时,就不是这样的了。
嵌套类意味着
1、要创建嵌套类的对象,并不需要其外围类的对象
2、不能从嵌套类的对象中访问非静态类的外围类对象
public class SomeTest {
private String somere="ewq";
public static class Atest{
private String wq = "关闭内部类";
public void testq() {
System.out.println(wq);
System.out.println();
}
}
private static class Atest2{
private String wq = "关闭内部类2";
public void testq() {
System.out.println(wq);
System.out.println();
}
}
public static void main(String[] args) {
Atest a = new Atest();
a.testq();
}
}
上面Atest和Atest2都是静态内部类,当编译成class时,Atest是单独在外面的和普通的类没有什么区别,当时Atest2是SomeTest$Atest2.class也就是说Atest2是内嵌在someTest里面的。其中两个类都是无法访问外部类的非静态资源。
接口内部的类
“`java
public interface NertTest {
void test2();
class Test2 implements NertTest{
public void test2() {
// TODO Auto-generated method stub
System.out.println("testttt");
}}
}
“`
接口中的任何资源都是默认为static public的,所以如果在里面放置一个类的话,他也将默认设置为这个,也就是嵌套类,public的嵌套类就是普通的类,和外部类已经没有多大关系了。
为什么使用内部类:
一般来说内部类继承自某个类,或者实现某个接口。内部类的代码操作创建他的外围类对象,所以可以认为内部类提供了某种进入其外围类的窗口。
每个内部类都能独立的继承某个接口,所以无论外围类是否已经继承了某个接口的实现,他对于内部类都没有影响
如果你想在你的类中同时实现几个接口的功能,以及返回几个接口的对象,就应该使用内部类。
闭包:是一个可调用的对象,它记录了一些信息,这些信息来自于创建他的作用域。闭包也就是他拥有创建他的作用域的一些信息。
所以内部类就是一种对象中的闭包,他自动拥有外部类(创建他的作用域)的应用,还有一些外部类的信息,他可以操作外部类的属性,包括private属性。
java通过内部类来解决回调的问题,但是真的是看不懂,就吧代码留在这里,懂了在来看
interface Incrementable {
void increment();
}
// Very simple to just implement the interface:
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
System.out.println(i);
}
}
class MyIncrement {
public void increment() {
System.out.println("Other operation");
}
static void f(MyIncrement mi) {
mi.increment();
}
}
// If your class must implement increment() in
// some other way, you must use an inner class:
class Callee2 extends MyIncrement {
private int i = 0;
public void increment() {
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable {
public void increment() {
// Specify outer-class method, otherwise
// you'd get an infinite recursion:
Callee2.this.increment();
}
}
Incrementable getCallbackReference() {
return new Closure();
}
}
class Caller {
private Incrementable callbackReference;
Caller(Incrementable cbh) {
callbackReference = cbh;
}
void go() {
callbackReference.increment();
}
}
public class Callbacks {
public static void main(String[] args) {
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrement.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
内部类与控制框架:
控制框架是一类特殊的应用程序框架,他用来解决响应事件的需求,主要用来响应事件的系统被称作事件驱动系统。
event.java
```java
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() { // Allows restarting
eventTime = System.nanoTime() + delayTime;
}
public boolean ready() {
return System.nanoTime() >= eventTime;
}
public abstract void action();
} /// :~
“`
controller.java
public class Controller {
// A class from java.util to hold Event objects:
private List eventList = new ArrayList();
public void addEvent(Event c) {
eventList.add(c);
}
public void run() {
while (eventList.size() > 0)
// Make a copy so you're not modifying the list
// while you're selecting the elements in it:
for (Event e : new ArrayList(eventList))
if (e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
} /// :~
上面的的两个类是为了实现对一个事件的不同表现形式的空置,如果你想实现不同的表现形式就应该继承event然后实现不懂的action方法,这样会使真个代码变的很大,很烦躁
public class GreenhouseControls extends Controller {
private boolean light = false;
public class LightOn extends Event {
public LightOn(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here to
// physically turn on the light.
light = true;
}
public String toString() {
return "Light is on";
}
}
public class LightOff extends Event {
public LightOff(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here to
// physically turn off the light.
light = false;
}
public String toString() {
return "Light is off";
}
}
private boolean water = false;
private String thermostat = "Day";
public class ThermostatNight extends Event {
public ThermostatNight(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Night";
}
public String toString() {
return "Thermostat on night setting";
}
}
public class ThermostatDay extends Event {
public ThermostatDay(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Day";
}
public String toString() {
return "Thermostat on day setting";
}
}
public class Restart extends Event {
private Event[] eventList;
public Restart(long delayTime, Event[] eventList) {
super(delayTime);
this.eventList = eventList;
for (Event e : eventList)
addEvent(e);
}
public void action() {
for (Event e : eventList) {
e.start(); // Rerun each event
addEvent(e);
}
start(); // Rerun this Event
addEvent(this);
}
public String toString() {
return "Restarting system";
}
}
public static class Terminate extends Event {
public Terminate(long delayTime) { super(delayTime); }
public void action() { System.exit(0); }
public String toString() { return "Terminating"; }
}
上面的类在think in java中写的很多,我删除了一些,只是做一些理解,这样实现一个比较大的控制器,然后内部类的形式继承自event然后实现不同的表现方法。 可以明显的增加对事件等的空置 让项目变得更加优雅。
class WithInner {
class Inner {}
}
public class InheritInner extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
} ///:~
要想继承一个内部类必须在构造函数中调用内部类的外部类然后调用内部类的构造函数,才能够实现构造。
内部类标识符
内部类是外部类名加上$再加上内部类 如果是匿名内部类那就用数字代替内部类名
Count.class
Count$Test.class
Count$1.class
内部类是可以存在于接口中,接口中的内部类 是static 也就是说,和嵌套类是一样的效果,也是可以独立直接使用的。他编译后是独立存在的
public interface NertTest {
void test2();
class Test2 implements NertTest{
public void test2() {
// TODO Auto-generated method stub
System.out.println("testttt");
}}
}
如果一个程序包含固定数量的且生命周期都是已知对象,那么这是一个非常简单的程序
java容器提供了晚上的方法保存对象,你可以使用这些工具来解决数量惊人的问题。
容器的基本类型 List Set Queue和Map
java容器类类库的基本用途是“保存对象”,将其划分为两个不同的概念。
1、Collection。一个独立元素序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,Set不能有重复元素,Queue按照队列的排规则来确定对象产生的顺序。
2、Map 一组成对额键值对 对象,允许你使用键来查找
List apple = new ArrayList();
上面new 的arraylist向上转型为List,如果arrayList中包含很多list没有的方法,如果你想使用这些方法向上转型就是不能够达到的。就必须使用本类的然后new的也是本类。
Collection借口概括了序列的概念,一种存放一组对象的方式。
添加元素
public static void test2() {
Collection collection = new ArrayList(Arrays.asList(1,2,3,4));
Integer[] moreIn= {6,7,8};
collection.addAll(Arrays.asList(moreIn));
Collections.addAll(collection, 12,23,34);
List list = Arrays.asList(16,17,18,19);
list.set(1,21);
for(Integer in:collection) {
System.out.print(in+" ");
}
System.out.println("");
for(Integer ins:list) {
System.out.print(ins+" ");
}
}
collection集合有自己的addAll用于添加一组元素
Collections.addAll(collection,1,2,3,4)
Collections.addALl(collection,mor)//more 是一个数组 不能是集合
list.set(1,21);//这个是List类中拥有的修改某个参数的位置的值
Collections工具类向一个集合类里面添加一组元素
ArrayList LinkedList都是list类型,不同的是在执行某一类型的操作的时候,拥有不同的效率。
HashSet TreeSet LinkedHashSet,HashSet是最快的的获取方式。储存顺序很重要就使用TreeSet,LinkedHashSet,它是按照被添加的顺序保存对象
HashMap TreeMap LinkedHaspMap,HashMap提供了最快的炒作技术,LinkedHashMap按照插入顺序保存键,同时还保留HashMap的查询速度。
基本的ArrayList,它用于随机访问元素,但是在List的中间插入和移除元素时比较慢
LinkedList,它通过代价较低的在List中间进行插入和删除操作,但是它在随机访问比较慢,但是它的特性集较ArrayList大
Collections.shuffle()//打乱原来的顺序
Collection.sort()//从小到大排列
retainAll()获取本list和另外的list的合计
迭代器:迭代器统一了对容器的访问方式。迭代器能够将遍历序列的操作与序列底层结构分离。
迭代器的工作主要是遍历并选择序列中的对象,java中迭代器 Iterator只能够单向移动。
next() 返回序列中下一个元素
hasNext()是否还有下一个元素
remove()将迭代器新近访问的的元素删除 这就是迭代器的一个重大作用能够实现安全删除
有了Iterator你就不用关心容器中的元素,如果你只是想遍历一下list而不做任何修改可以直接使用foreach。这就是说如果想在遍历的时候做一些删除就应该使用i或者迭代器不能够使用foreach。
ListInteror:只能用于访问list,可以双移动,可以使用set()方法代替它访问的最后一个元素,也就是代替当前的元素。可以调用listIterator(n)方法创建一个开始就指向索引为n的元素处。
ite.previous();向前移动
ite.hasPrevious();是否还有前一个节点
LinkedList实现插入移除等操作的时候效率比较高,但是随机访问就比较弱
LinkedList可以用来实现栈、队列,双端队列等
常用栈不适用java.util.Stack类,因为他的设计是有缺陷的,最好使用LinkedList实现自己的栈(后进先出)。
栈的一些方法
push() 将元素压栈
peek()返回栈顶元素,但是不删除
pop()返回栈顶元素,并删除栈顶
LinkedList实现了Queue,所以可以使用这个来当做一个队列
Queue(先进先出)相关的方法
offer() 它在允许的情况下,将一个元素插入到队尾或者返回false
peek() 不移除的情况下返回队头,如果队列为空返回null
element() 不移除的情况下返回对头,如果队列为空返回NoSuchElementExecption
poll() 移除对头并返回队头,对列为空返回null
remove() 移除并返回对头,队列为空返回NoSuchElementExecption
Priorityqueue利用Comparator可以实现优先级队列,具体怎么实现,还是不太清楚,这里留一点。
Collections.reverseOrder()用于产生反序的Comparator
Set不保存重复的元素。
map可以返回key的set,返回value的collection,也就是key是不能重复的。而value是可以重复的。
所有实现Iterable接口的,然后返回了Iterator对象,然后就能够使用foreach
新程序不应该使用Vector Hashtable和Stack
java的基本理念是 结构不佳的代码不能运行
throw 抛出异常
try{
//捕获异常
}
printStackTrace()可以打印异常栈,getStackTrace()可以直接用来获取异常栈。
Error用来表示编译和系统错误,exception是可以被抛出的基本异常,是会被检查的异常,程序员只用关系exception。RuntimeException不收捕获的异常。一般不在代码中捕获。
使用finally进行清理,无论try里面发生了什么finally总是会被执行。
不可变String,每一个修改String的方法,实际上都是创建一个全新的String,而对原来的String没有做任何改变。
java编译后的伪代码
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String reee
2: astore_1
3: new #18 // class java/lang/StringBuilder
6: dup
7: aload_1
8: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #26 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V
14: ldc #29 // String wqwqwww
16: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: getstatic #39 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
}
java源码
public static void main(String[] args) {
String re= "reee";
re+="wqwqwww";
System.out.println(re);
}
重上面的java代码
可以看出java string相加是用的StringBuilder的append方法,然后使用的是toString生产string,编译器这样使用应为stringBuilder的效率比较高
StringBuilder是java5引进的,在之前用的事StringBuffer,StringBuffer是线程安全的,所以StringBuilder是效率更高。
System.out.format() 可以格式化输出。
formatter类,可以将formatter看做是一个翻译器将字符串和一些数据翻译成需要的结果。
Formatter fo = new Formatter(System.out);
fo.format("%s jjiji", "ree");
格式化后代码 然后按照够着formatter的流输出。
运行时类型信息使得你可以在程序运行时发现和使用类型信息。
面向对象编程中基本的目的是:让代码只操作对基类的引用。
RTTI:在运行时,识别一个对象的类型。
类是程序的一部分,每个类都有一个Class对象。在编译一个新类的时候就会生成一个Class对象。
所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造器也是静态方法,即使在构造器之前并没有使用static关键字(反编译出来的java汇编代码,上面也没有任何标识构造器是汇编的)。因此,使用new操作符创建新类对象的时候也会被当做对类的静态成员的引用。
Class对象仅在需要的时候才被加载,static初始化在类加载的时候进行。
Class对象就和其他对象一样,我们可以获取并操作他的引用,这是类加载器的工作。
Class.newInstance()无法加载构造器是私有类型的
Class.forName()会加载然后初始化该类,如果要是使用的
Class的newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明“我不知道你的确切类型,但是无论如何要正确地创建你自己”。使用newInstance来创建的类,必须带有默认构造器,不光是私有类型的够着器不能用这个来生成对象,如果没有默认构造器(无参构造器)也是不能够正确生成的。
使用FancyToy.class来获取一个CLass对象的步骤
1.加载 这是有类加载器执行的,该步骤将查找字节码,并从这些字节吗中创建一个Class对象。
2.链接 在链接阶段将验证类中的字节码,为静态域分配储存空间,并且如果需要的话,将解析这个类创建的对其他类的引用
3.初始化 如果该类具有超累,则对其初始化,执行静态初始化器,和静态初始化代码块。
使用.class(类标记)来获取Class引用不会引起初始化,而使用Class.forName()就会引起初始化。
如果一个static final值是 编译期常量,那么这个值不需要对这个类进行初始化。如果一个static域不是final的,那么在对他访问时总是要求它在被读取之前,要先进行链接(为这个域分配储存空间)和初始化(初始化该储存空间)
想Class中加入泛型语法仅仅是为了提供编译期类型检查,因此如果你操作有误,稍后会立即发现这一点,如果是在普通的Class引用需要在运行的时候才能够检查出错误。
RTTI形式
1.传统的类型转换,如果失败将会抛出一个ClassCastException异常
2.代表对象的类型的Class对象,通过查询Class对象可获取运行是所需的信息
3.使用关键字instanceof(造型前检查),它将返回一个boolean值,告诉我们该类型是不是某个特定的类型
对象 instanceof 类,然后返回一个布尔类型,动态instanceof 是Class提供的,Class.isInstance(object)返回一个boolean类型检测一个对象是不是某个类的。
ch.getClass() == Parents.class 使用这样的语句(ch的类型是Patents类型的子类)在编译期的时候就会出错。但是好像老版本可以这样使用使用equals()方法是可行的。
如果你不知道某个对象的确切类型,RTTI可以告诉你,但是必须这个类型在编译时必须已知,这样才能使用RTTI识别他,并利用这些信息做一些有用的事
反射:
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Filed,Method以及Constructor类,这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象有关的方法。另外,还可以调用getFields()、getMethods(),getConstructor()等很便利的方法,用于返回表示字段、方法以及构造器的最想数组。这样,匿名对象的类信息就能够在运行期被完全确定下来,而在编译时不需要知道任何事情。
对于反射机制.class文件在编译时是不可获取的,所以是在运行是打开和检查.class文件。
代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替实际对象的对象。这些操作通常设计与“实际对象”的通信,因此代理通常充当着中间人的角色。
interface Interface{
void doSomething();
void doSomethingElse(String arg);
}
class RealObject implements Interface{
public void doSomething() {
System.out.println("dosomething");
}
public void doSomethingElse(String arg) {
System.out.println("do something else "+arg);
}
}
class SimpleProxy implements Interface{
private Interface proxied;
public SimpleProxy(Interface interface1) {
this.proxied = interface1;
}
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("simple do something");
proxied.doSomething();
}
public void doSomethingElse(String arg) {
// TODO Auto-generated method stub
System.out.println("simple dosomething else "+arg);
proxied.doSomethingElse(arg);
}
}
public class SimpleProxyDemo {
public static void consumer(Interface interface1) {
interface1.doSomething();
interface1.doSomethingElse("booo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
只要你想要将额外的操作从实际对象中分离到不同的地方,特别是当你希望能够很容易的做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用。
代理最大的作用是你可以有计划的、可控的、可度量去控制对象的创建,方法的调用。
public class DynamicProxyHandler implements InvocationHandler{
private Object proxied;
public DynamicProxyHandler(Object prObject) {
this.proxied = prObject;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("*****proxy:"+proxy.getClass()+".mothod"+method+",args "+args);
if(args!=null) {
for(Object arg : args) {
System.out.println(" "+arg);
}
}
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void cousumer(Interface interface1) {
interface1.doSomething();
interface1.doSomethingElse("bobo");
}
public static void main(String[] args) {
RealObject ro = new RealObject();
cousumer(ro);
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[] {Interface.class}, new DynamicProxyHandler(ro));
cousumer(proxy);
}
}
通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载其,然后传递给它),一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器,因此通常想调用处理器的构造器传递一个“实际”对象的引用,从而是的调用处理器在执行其中介任务时,可以将请求转发。
大部分代码尽可能少的了解对象的具体类型,而只是与对象家族中的一个通用表示打交道,这样代码会更容易些,更容易读,且更便于维护,设计也更容易实现,理解和改变。所以多态是面向对象编程的基本目标。
这里真的是看不懂,什么时候要重复的看一下
interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合型,但是在Java中也可以使用RTTI然后然后强制向下转型,这样也不会出现问题,但是这样就不能够保证你的代码耦合度超过你的想象。
关于耦合度的概念应该多理解一下
关于反射这一节,应该花更多时间去学习一下
一般的类和方法,只能够使用具体的类型,要么是基本类型,要么是自定义的类,如果要编写可以应用与多种类型的代码,这种刻板的限制对代码的束缚就会很大。
如果类的构造器的类是private的,那么这个类也就不能够扩展。
泛型实现了参数化类型的概念,使代码可以应用于多种类型,泛型这个术语的意思是,适用于许多许多的类型。
泛型的主要目的之一就是用来制定容器要持有什么类型的对象,而且编译器来保证类型的正确性。
Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
元组(tuple)它是将一组对象直接打包储存于其中的一个单一对象,这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。
public class TwoTuple<A,B>{
public final A first;
public final B second;
public TwoTuple(A a,B b) {
first =a;
second=b;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "("+first+","+second+")";
}
}
这种同时返回两个类型以上的叫成为元组,一般只允许读取,不允许添加。
public class LinkedStack<T> {
private static class Node<U>{
U item;
Node next;
Node(){
item = null;
next=null;
}
Node(U item,Node next){
this.item = item;
this.next = next;
}
boolean end() {
return item==null && next==null;
}
}
private Node top = new Node();
public void push(T item) {
top = new Node(item,top);
}
public T pop() {
T result = top.item;
if(!top.end()) {
top = top.next;
}
return result;
}
public static void main(String[] args) {
LinkedStack lss = new LinkedStack();
lss.push("211");
lss.push("211we");
lss.push("211wq");
lss.push("211q");
System.out.println(lss.pop());
}
}
使用java 泛型机制实现的自定义栈
泛型方法使得该方法能够独立于类而产生变化。无论何时,只要你能做到,你就应该精良使用泛型方法,也就是说,如果使用泛型方法可以取代整个类的泛型化,那么就应该只使用泛型方法。
List re = new ArrayList<>();
钻石语法在java7之前的泛型是不支持的
ArrayList.class是正确的但是ArrayList.class却是错误的。
原因是,在泛型代码内部,无法获得任何有关泛型参数类型的信息。
你可以知道诸如类型参数标识符和泛型类型便捷这类的信息,你却无法知道用来创建某个特定实例的实际的类型参数
java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此List和List在运行时事实上是相同的类型。这两种形式都被擦除成他们的“原生”类型,即List。
LinkedStack
泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为他们非泛型上界,就是上面那个T extends Apps在编译时将会被替换为Apps,而一般的将被替换为Object
因为使用的事擦除,所以泛型不能够显式的应用运行时类型的操作中,例如转型、instanceof操作和new表达式。因为所有有关参数的类型信息都丢失了,无论何时,当你在编写泛型代码时,必须时刻提醒自己,你只是看起来好像拥有有关参数的类型信息而已。
Array.newInstance(componentType, length)
用于泛型创建数组
泛型不能够直接new T(),原因是擦除和不能够验证T具有默认构造器。为了能够创建T的实例java推荐使用工厂模式
泛型边界
边界使得你可以用于泛型的参数类型上设置限制条件,尽管这是的你可以强制规定泛型可以应用的类型,但是其潜在的一个更重要的效果是你可以按照自己的边界类型来调用方法。
在java中数组是一种效率最高的存储和随机访问对象引用序列的方式。ArrayList的效率比数组要低的多。
Arrays.deepToString(a)将类组成的数组转换成是string,同时也可以转换多维数组。
Arrays.toString()将基本类型的数组转换为string
多维数组可以理解成数组里面存放的还是数组。
Arrays.fill(a2, 12); 用于填充一个数组用同一个值,例如这里会把数组a2全部填充成12
System.arraycopy()复制一个数组,速度比for要快。不会自动包装和自动拆包,所以两个数组的类型必须相同。
Arrays.equals()用于比较两个数组的值是否相同,他是将两个数组的相同位置使用equal()进行比较,如果是基本类型那就使用包装类的equal,如果使用的是equals()则是比较的栈上的地址所以会是false。
使用内置的排序方法,可以对任意的基本类型进行排序,也可以对任意的对象数组进行排序,只要对象实现了Comparable或者是相关联的Compartor
如果数组已经排好了,就可以使用Arrays.binarySearch()执行快速查找,但是没有排序的使用这个会出现不可预料的结果。他查找然后返回查找的在数组中的位置如果返回时-1就是表示没有查找到。