●向上转型,向下转型,
向上转型,就是子类转型成父类。如: 麻雀是鸟, 这是非强制的说法; 向下转型,就是父类转型成子类。如: 鸟是麻雀, 这是强制的说法; |
● Java中不被static修饰的方法,
如果去掉mul()方法的static修饰符, 第六行会提示: Cannot make a static reference to the non-static method mul(int) from the type demo |
1:没有static修饰的方法,在调用的时候需要先创造对象 类名 对象名=new 类名(); 对象名.方法名(); 2:有static修饰的方法,在调用的时候直接调用 类名.方法名(); 也就是说: 没有static修饰的,它们在生成的时候,就属于对象。 有static修饰的,它们在生成的时候,就属于类。 main方法是java自带的,我们创建它的时候,就已经注定了它的必然性——静态方法。 在静态方法中,只能访问静态的变量,还有静态的其他方法。 |
static方法只能访问static方法,非static方法可以访问static方法, 原因: static是属于类的,而非static的普通方法是属于对象的。 属于类的静态方法可以在对象不存在的时候就能访问到,而普通方法必须先new一个对象才能用这个对象访问。 |
为什么main()方法是static的? 因为在程序开始执行时必须调用main()。 我们知道,在C/C++当中,这个main方法并不是属于某一个类的,它是一个全局的方法,所以当我们执行的时候,c++编译器很容易的就能找到这个main方法,然而当我们执行一个java程序的时候,因为java都是以类作为程序的组织单元,当我们要执行的时候,我们并不知道这个main方法会放到哪个类当中,也不知道是否是要产生类的一个对象,为了解决程序的运行问题,我们将这个main方法定义为static,这样的话,当我们在执行一个java代码的时候,我们在命令提示符中写:java Point(Point为一个类),解释器就会在Point这个类当中,去调用这个静态的main方法,而不需要产生Point这个类的对象,当我们加载Point这个类的时候,那么main方法也被加载了,作为我们java程序的一个入口。 |
● JavaBean的解释
JavaBean是符合某种规范的Java组件,也就是一个可以重复使用的Java类, 这个类创建的一个对象称做一个Bean。这个类具有如下特点: 1. 创建JavaBean类时必须带有包名, 且必须实现序列化接口; ※ Java的"对象序列化"能让你将一个实现了Serializable接口的对象转换成一组Byte,这样日后要用这个对象时候,你就能把这些byte数据恢复出来,并据此重新构建那个对象了。 2. 必须具有一个访问属性为public的无参构造函数; 3. 类中所有的属性都必须定义为私有的, 所有方法都必须定义为公有的; 4. 私有化的属性必须通过public类型的方法暴露给其他程序,并且方法的命名也必须遵守一定的命名规范, 具体来说: ① 如果属性名是xxx,那么为了更改或获取属性,在类中可以使用两个public方法: ●getXXX():用来获取属性xxx。 ●setXXX():用来修改属性xxx.。 ② 对于boolean类型的成员变量,允许使用"is"代替上面的"get"和"set", 如。
JavaBean的作用: JavaBean可以非常好地实现控制逻辑、业务逻辑、表示层之间的分离,从而大大降低了它们之间的耦合度。
内省:把一类中需要进行设置和获得的属性访问权限设置为private(私有的)让外部的使用者看不见摸不着,而通过public(共有的)set和get方法来对其属性的值来进行设置和获得,而内部的操作具体是怎样的?外界使用的人不用知道,这就称为内省。 |
JavaBean案例: |
package JavaBean; import java.io.Serializable;
public class JavaBeanDemo implements Serializable { // 实现了Serializable接口 public JavaBeanDemo() { } // 无参的构造方法
private int id; // 私有属性Id private String name; // 私有属性name
public int getId() { return Id; }
public void setId(int id) { // set()方法 this.id = id; }
public String getName() { // get()方法 return name; }
public void setName(String name) { this.name = name; } } |
JavaBean更实际的案例: 假如有人要用Java实现一个单向链表类,可能会这样写: // 编译成 java-int-list_1.0.jar public final class JavaIntList { static class Node { public Node next; public int value; } public Node head; public int size; } 上述实现为了能够快速获取链表的大小,把链表大小缓存在size变量中。用法如下: JavaIntList myList = new JavaIntList(); System.out.println(myList.size); JavaIntList的作者很满意,于是开源了java-int-list库的1.0版。文件名是java-int-list_1.0.jar。发布后,吸引了许多用户来使用java-int-list_1.0.jar。 // 编译成 java-int-list_2.0.jar public final class JavaIntList { static final class Node { public Node next; public int value; } public Node head; public int getSize() { Node n = head; int i = 0; while (n != null) { n = n.next; i++; } return i; } } 然后发布了2.0版:java-int-list_2.0.jar。发布后,原有java-int-list_1.0.jar的用户纷纷升级版本到2.0。这些用户一升级,就发现自己的程序全部坏掉了,说是找不到什么size变量。于是这些用户就把作者暴打一顿,再也不敢用java-int-list库了。 private int size; public int getSize() { return size; } 让用户一开始就使用getSize,以便有朝一日修改getSize实现时,不破坏向后兼容性。这种public int getSize() { return size; }的惯用手法,就是Java Bean。 |
● 构造函数的访问属性
public 常用 private 单例 static 不可以(Java中静态的东西都是属于类的,为类服务,构造函数是为了初始化对象,为对象服务) |
●如果子类没有重写父类的方法,那么supper.父类方法()和this.父类方法()的作用是一样的。
重写父类的方法以后, super用来调用父类的方法,this调用子类重写的方法。 属性的情况是一样的。 |
public class F{//定义父类 public void s(){//定义父类方法s } } public class K extends F{//定义子类K,继承父类F public void s(){//定义子类方法s,覆盖父类s } public void k2(){ super.s();//通过super,指明调用父类的方法s this.s();//通过this,指明调用当前类的方法s } } |
● super和this的异同:
1)super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句) 2)this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句) 3)super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参) 4)this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名) 5)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。 6)super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。 7)super()和this()均需放在构造方法内第一行。 8)尽管可以用this调用一个构造器,但却不能调用两个。 9)this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。 10)this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。 11)从本质上讲,this是一个指向当前类的对象的指针, 然而super是一个Java关键字。 |
●程序绑定的概念:
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定.
静态绑定: 在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),此时由编译器或其它连接程序实现。例如:C。 针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,java当中的方法只有final,static,private和构造方法是前期绑定
动态绑定: 后期绑定:在运行时根据具体对象的类型进行绑定。 若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。 动态绑定的过程: 虚拟机提取对象的实际类型的方法表; 虚拟机搜索方法签名; 调用方法。 |
Java中的变量都是静态绑定的,方法的话只有static和final(所有private默认是final的)是静态绑定的. 注:类的private方法会隐式地被指定为final方法。 |
●实例变量 & 静态变量
实例变量必须创建对象后才可以通过这个对象来使用; 静态变量则可以直接使用类名来引用。 |
● 类只加载一次
如果不是有特殊用途,每个类在虚拟机中被加载并且只加载一次。 |
什么时候会加载类? 有三种情况 1.创建对象:new StaticCode(); 2.使用类中的静态成员:StaticCode.num=9; StaticCode.show(); 3.在命令行中运行:java StaticCodeDemo |
●静态代码块、构造代码块和构造函数的区别
静态代码块:用于给类初始化,类加载时就会被加载执行,只加载一次。 构造代码块:用于给对象初始化的。只要建立对象该部分就会被执行,且优先于构造函数。 ※ 直接在类中定义且没有加static关键字的代码块称为{}构造代码块。 构造函数: 给对应对象初始化的,建立对象时,选择相应的构造函数初始化对象。 |
//静态代码块案例: package demo;
class Parent { static String name = "hello"; { System.out.println("parent block"); } static { System.out.println("parent static block"); }
public Parent() { System.out.println("parent constructor"); } }
class Child extends Parent { static String childName = "hello"; { System.out.println("child block"); } static { System.out.println("child static block"); }
public Child() { System.out.println("child constructor"); } }
public class StaticIniBlockOrderTest {
public static void main(String[] args) { new Child(); } }
parent static block child static block parent block parent constructor child block child constructor |
对象的初始化顺序:首先执行父类静态的内容, 父类静态的内容执行完毕后,接着去执行子类的静态的内容, 当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块, 父类的非静态代码块执行完毕,接着执行父类的构造方法; 父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。 总之一句话,静态代码块内容先执行(父类静态的内容执行完毕后,接着去执行子类的静态的内容),接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。 |
再如:
|
●为什么要用父类引用指向子类对象?
Shape s = new Circle(5,6,4);
可以用这几个关键词来概括:多态、动态链接、向上转型
例如父类person有子类men和women, 现在要写一个对象数组,其元素包括子类men类型和women类型的, 我们写成: person[] a=new person[]{new men("小明"),new women("小红")}; 这其实就是一个父类引用指向子类对象, 如果用子类是不行的. |
● 抽象类和接口的异同
相同点 都不能被直接实例化,但可以定义抽象类或接口类型的引用变量,并且可以通过继承实现其抽象方法。 都是面向抽象编程的技术基础,实现了诸多的设计模式。
不同点 抽象类不能实现多继承;接口支持多继承。 抽象类既可以定义抽象方法,也可以定义非抽象方法;接口只能定义抽象方法。 抽象类可以有普通数据成员,接口不可以有普通数据成员(只能有全局常量)。
形象比喻 接口是对动作的抽象,抽象类是对根源的抽象。 抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。 人可以吃东西,狗也可以吃东西,你可以把"吃东西"定义成一个接口,然后让这些类去实现它. 所以,在高级语言上(不包括C++和Python),一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。
接口函数就是某个模块写的主要用来给其它模块调用的函数。 简单的说接口函数就是一个类中的公有函数, 它提供程序与类的私有成员之间的接口, 一个对象可以通过它来访问类私有成员. 例如: SendMessage(...); 是一个发消息的函数,我们无须知道它是怎么实现的,只需要知道他能实现向某个东西发送消息即可.
举个例子,搅拌机是一个类,把苹果,橘子,梨等水果(参数)放进去(发送消息)就出来果汁(结果,返回值)了. class 搅拌机 { private: 零件; public: 饮料 搅拌(水果); // 这个就是接口 }; int main(void) { 搅拌机 A = new 搅拌机; 饮料 苹果汁 = A-> 搅拌(一个黄元帅,两个红富士);// 调用接口 delete A; return 0; }
这就是接口,接口对应的就是实现。
每一个*.class的文件都对应一个类或则接口,*.class文件是JVM真正能读懂的文件格式。
//下面的不同点暂时不用深入 接口是一组行为规范;抽象类是一个不完全的类,着重族的概念。 接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。 接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。 |
//抽象类(abstract class)的定义方式如下: public abstract class AbstractClass //抽象类里面至少有一个抽象方法 { public int t; //普通数据成员 public abstract void method1(); //抽象方法,抽象类的子类在类中必须实现抽象类中的抽象方法 public abstract void method2(); public void method3(); //非抽象方法 public int method4 (){ //抽象类中可以赋予非抽象方法方法的默认行为,即方法的具体实现 } }
//接口(interface)的定义方式如下: public interface Interface { static final int i; //接口中不能有普通数据成员,只能够有静态的不能被修改的数据成员,static表示全局,final表示不可修改,可以不用static final 修饰,因为会隐式的声明为static和final public void method1(); //接口中的方法一定是抽象方法,所以不用abstract修饰 public void method2(); //接口中不能赋予方法的默认行为,即不能有方法的具体实现 } |
● 理解"一个类可以实现多个接口,但一个类不能实现多继承"
一个类可以实现多个接口: 接口成员均为全局常量和抽象方法, 即使不同接口存在同名抽象方法,并不会造成方法一个类不能实现多继承: 如果父类中存在同名但不同实现非抽象方法,多继承就造成了混乱 |
● Java中String类的内存分配
1 物理的内存是线性结构,并不存在拥有不同功能的不同区域。 编译器(或者JVM)为了更高效地处理数据,会用不同的算法把内存分为各种区域,不同的区域拥有各自的特性,Java中,内存可以分为栈,堆,静态域和常量池等。(可能有不同的叫法,但逻辑是一致的)
2 不同内存区域的功能和特点: 栈区:存放局部变量(变量名,对象的引用等)特点:内存随着函数的调用而开辟,随着函数调用结束而释放。 堆区:存放对象(也就是new出来的东西)特点:可以跨函数使用,每个对象有自己对应的存储空间。 静态域:存放在对象中用static定义的静态成员。 常量池:存放常量。(常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。)
3 定义String的方法: 1,String str1 = "hello"; 2,String str2 = new String("hello"); 第一种方法:引用str1被存放在栈区,字符串常量"hello"被存放在常量池,引用str1指向了常量池中的"hello"(str1中的存放了常量池中"hello"的地址)。 第二种方法:引用str2被存放在栈区,同时在堆区开辟一块内存用于存放一个新的String类型对象。(同上,str2指向了堆区新开辟的String类型的对象) 如下图: Java中String类的内存分配
4 这两种方法的区别是什么? 第一种:常量池的字符串常量,不能重复出现,也就是说,在定义多个常量时,编译器先去常量池查找该常量是否已经存在,如果不存在,则在常量池创建一个新的字符串常量;如果该常量已经存在,那么新创建的String类型引用指向常量池中已经存在的值相同的字符串常量,也就是说这是不在常量池开辟新的内存。 String str1 = "hello"; String str2 = "hello"; 示意图如图1 第二种:在堆中创建新的内存空间,不考虑该String类型对象的值是否已经存在。换句话说:不管它的 只是多少,第二种方法的这个操作已经会产生的结果是:在堆区开辟一块新的内存,用来存放新定义的String类型的对象。 String str1 = new String("hello"); String str2 = new String("hello"); 示意图如果2 Java中String类的内存分配 Java中String类的内存分配
5 下面用代码来测试: public class Test { public static void main(String[] args) { String str1 = "hello"; String str2 = "hello"; System.out.println(str1 == str2);//true System.out.println(str1.equals(str2));//true
String str3 = new String("hello"); String str4 = new String("hello"); System.out.println(str3 == str4);//false System.out.println(str3.equals(str4));//true System.out.println(str1 == str3);//false System.out.println(str2.equals(str3));//true //这里涉及到==和equals方法的区别,请看我的另一篇文章:《Java中,equals 与==的渊》http://jingyan.baidu.com/article/f96699bbc9d6ae894e3c1b81.html } } |
●如果在创建类的时候没有声明要继承的类 那么java就默认 将它继承于Object类
public class A{ /*Code*/ }
public class A extends java.lang.Object{ /*Code*/ } 以上两种的等价的public class Test // 从Object类继承 |
●Object中有3个非常重要的方法
hashcode(), toString(), equals() Sun都是建议我们写自己的类的时候,最好都重写上述3个方法 |
●所有类都从Object类继承。
如果自定义的类没有覆盖toString方法,则对象在调用toString方法时用的是Object类toString方法,返回的是:包名.类名@此对象哈希码的无符号十六进制表示。相当于: getClass().getName() + '@' + Integer.toHexString(hashCode())
如果equals没有被覆盖,作用则是判断两个对象是否相同。
如果hashCode()没有覆盖,返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 |
●toString方法
●hashCode( )
1)hashCode:创建的对象在堆内存中的地址值 经过一系列非常复杂的算法转化而来的int类型的数值,它是一种用于查找的索引值(一对一或多对一的关系),对开发来说无意义。 2)如果两个对象相等话, 第①个条件:equals返回的结果必须是true 第②个条件:必须要有相等的hashCode 3)如果两个对象不相等,则hashCode值一定不等 4)判断对象是否相等的时候,必须要重写equals和hashCode
|
Java不能像C/C++一样直接查看内存地址, 但可借助OllyDbg, Cheat Engine等工具 |
c17164是12677476的十六进制 |
●equals方法
(1)s1 == s2为true,因为s1和s2都是字符串字面值"nihao"的引用,指向同一块地址,所以相等。 (2)s1 == s3为false,是因为通过new产生的对象在堆中,s3是堆中对象的引用,而是s1是指向字符串字面值"nihao"的引用,地址不同所以不相等。 |
●arraycopy(),
System提供了一个静态方法arraycopy(),我们可以使用它来实现数组之间的复制。其函数原型是: public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度。 |
●Java中string类concat方法和+的区别—concat()和+都是String类的方法
都可以将2个字符串拼接到一块,这一点2这功能相同。
但是 + 还可以将 字符串与非字符串(比如数字),拼接在一起,成为字符串。
|
●字符串池
Java虚拟机有一个字符串池,保存着几乎所有的字符串对象。 字符串表达式总是指向字符串池 中的一个对象。 使用new操作创建的字符串对象不指向字符串池中的对象, 但是可以使用intern方法使其指向字符串池中的对象. (注:如果池中已经有相同的 字符串--使用equals方法确定,则直接返回池中的字符串,否则先将字符串添加到池中,再返回)。池中两个相等的字符串如果使用"=="来比较将返回 真。 |
●==和equals
==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 |
●区别下面两种说法
while(in.readLine()!=null){ String aa=in.readLine(); } //第一个在读到while(in.readLine()!=null)的时候,如果有数据,那么执行String aa=in,readLine()的时候程序会继续往下读,也就是第一个while里面的读入被抛弃了,如果程序读入的只有是一行的话,那么这行就不会读入!!
while(true){ String aa=in.readLine(); } |
因此while(true) 是一个无限循环,因为表达式的值一直为真。
为了跳出循环,循环体内部要用break语句来跳出。 例如,可以在循环体内部用if来判断,if(x==5)break; |
● Java的正则表达式的运用
Java使用正则表达式需要涉及到Pattern和Matcher。 Pattern和Matcher之间的关系就好比Pattern是做模具的师傅,Pattern将模具(正则表达)做好之后,指派一个小工(matcher)去匹配,matcher要做的就是原材料(即要被匹配的源字符串)和模具(即Pattern中的正则表达式)配对、比较。
※ 回想学KMP算法时候学到的Pattern(模式串)
※ matcher.find()和 matcher.matches()的区别? find是部分匹配,matches是全部匹配
※ Pattern的静态方法matcher()的作用? Matcher matcher = pattern.mathcer(scanner.nextLine()) Pattern对象将会使用matcher()方法来生成一个Matcher实例,接着便可以使用这个 Matcher实例(以编译的正则表达式为基础)对目标字符串进行匹配工作,多个Matcher是可以共用一个Pattern对象的。 |
●StringBuilder
public StringBuilder delete(int start, int end) 参数 start -- This is the beginning index, inclusive. end -- This is the ending index, exclusive. |
●ArrayList的元素可以是……
//下面ArrayList的元素可以是 ArrayList list=new ArrayList(); list.add("北"); list.add("京"); list.add(0, "中"); list.add(1, "国"); System.out.println(list); System.out.println(list.indexOf("京")); System.out.println(list.get(2));
ArrayList list2=new ArrayList(); list2.add(new String("北")); list2.add(new String("京")); list2.add(0, new String("中")); list2.add(1, new String("国")); System.out.println(list); System.out.println(list.indexOf("京")); System.out.println(list.get(2)); |
[中, 国, 北, 京] 3 北 [中, 国, 北, 京] 3 北 |