类和对象
static修饰的成员不能访问没有static修饰的成员。
没有static修饰的成员变量和方法都必须使用对象来调用。
this可以代表任何对象,当 this出现在某个方法体中,它所代表的对象是不确定的,但它的类型是确定的,它所代表的对象只能是当前类;只有这个方法 被调用时,它所指代的对象才能被确定下来。谁在调用这个方法,this就代表谁。
形参个数可变的方法
public class Varargs
{
public static void test(int a,String...books){
//book被当成数组处理
for(String tmp: books){
System.out.println(tmp);
}
System.out.println(a);
}
public static void main(String[] args)
{
//
test(2,"疯狂java","和好good");
}
}
复制代码
长度可变的形参只能处于形参列表的最后,一个方法中最多只能包含一个长度可变的形参。
长度可变的形参本质就是一个数组类型的形参,因此调用包含一个长度可变的形参的方法时,这个长度可变的形参既可以传入多 个参数,也可以传入一个数组。
成员变量与局部变量
class Person
{
public String name;
public static int eyeNum;
}
public class PersonTest{
public static void main(String[] args){
System.out.println("Person的eyeNum类变量值:"+Person.eyeNum);
//创建Person对象
Person p=new Person();//系统创建了一个Person对象,并把这个对象赋给p变量,Person对象里包含了name
//实例变量,实例变量是在创建实例时分配内存空间指定初始值的。
System.out.println("p变量的name值是:"+p.name+"p对象的eyeNum变量值是:"+p.eyeNum);
p.name="bajie";
p.eyeNum=2;
System.out.println("p变量的name值是:"+p.name+"p对象的eyeNum变量值是:"+p.eyeNum);
System.out.println("Person的eyeNum类变量值:"+Person.eyeNum);
Person p2=new Person();
System.out.println("Person的eyeNum类变量值:"+p2.eyeNum);
}
}
复制代码
- 局部变量的初始化和内存中的运行机制
局部变量定义后必须经过显示初始化才能使用。因为定义局部变量后,系统知道等到程序为 这个变量赋值是才会为局部变量分配内存。并将初始值保存到这块内存中。
隐藏和封装
对于局部变量而言,其作用域就是其所在的方法,不可能被其他类所访问,因此不能使用访问控制符来修饰。构造器重载
有点乱,因为记的都是自己容易忘或者混淆的点,大标题小标题排的很乱。其他点另起一篇。
类的继承
Java的继承通过extends关键字来实现
1.1 重写父类的方法
方法的重写要遵循“两同两小一大”规则,“两同”即方法名相同、形参列表相同;
“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或者相等,子类方法声明抛出的异常类应比父类方法声明抛出的气场类更小或相等。
覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法。
1.2 super限定
如果需要在子类方法中调用父类被覆盖的实例方法,则可使用super限定来调用父类被覆盖的实例方法。
super是Java提供的一个关键字,super用于限定该对象调用它从父类继承得到的实例变量和方法。 正如this不能出现在static修饰的方法中一样,super也不能出现在static修饰的方法中。 static修饰的方法是属于类的,方法的调用者可能是一个类,而不是对象。
如果在构造器中使用super,则super用于限定该构造器初始化的是该对象从父类继承得到的实例变量,而不是该类自己定义的实例变量。
class BaseClass{
public int a=5;
}
public class SubClass extends BaseClass{
public int a=7;
public void accessOwner(){
System.out.println(a);
}
public void accessBase(){
//通过super来限定访问从父类继承得到的a实例变量
System.out.println(super.a);
}
public static void main(String[] args){
SubClass sub=new SubClass();
sub.accessOwner();
System.out.println();
sub.accessBase();
}
}
复制代码
如果子类里没有包含和父类同名的成员变量,那么在子类实例方法中访问该成员变量时,则无需显示使用super或父类名作为调用者。
如果在某个方法中访问名为a的成员变量,但没有显式指定调用者,则系统查找a的顺序为:
(1)查找该方法中是否有名为a的局部变量。
(2)查找当前类中是否包含名为a的成员变量.
(3)查找a的直接父类中是否包含名为a的成员变量,依次上溯a的所有父类,直到java.lang.Object类,如果最终不能找 到名为a的成员变量,则系统出现编译错误。
如果被覆盖的是类变量,在子类方法中则可以通过父类名作为调用者来访问被覆盖的类变量。
子类实例变量隐藏父类实例变量
class Parent{
public String tag="study java";
}
class derived extends Parent{
private String tag="study study java";
}
public class HideClass {
public static void main(String[] args){
derived d=new derived();
//System.out.println(d.tag);程序不可访问d私有变量tag,所以会引起编译错误
//将d变量显式向上转型为Parent后,可访问tag实例变量
System.out.println(((Parent)d).tag );
}
}
复制代码
1.3 调用父类构造器
class Base{
public double size;
public String name;
public Base(double size,String name){
this.name=name;
this.size=size;
}
}
public class Sub extends Base {
public String color;
public Sub(double size,String name,String color){
super(size,name);
this.color=color;
}
public static void main(String args[]){
Sub s=new Sub(5.6,"test","blue");
System.out.println(s.size+" "+s.name+" "+s.color );
}
}
复制代码
使用super调用和使用this调用也很像,区别在于super调用的是其父类构造器,而this调用的是同一个类中重载的构造器。
因此,使用super调用父类构造器也必须出现再子类构造器执行体的第一行,所以this调用和super调用不会同时出现。
不管是否使用super调用来执行父类构造器的初始化代码,子类构造器总会调用父类构造器一次。子类构造器调用父类构造器分如下几种情况:
· 子类构造器执行体的第一行使用super显式调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器。
· 子类构造器执行体的第一行使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表调用里传入的实参 列表调用本类的另一个构造器。执行本类中另一个构造器。执行本类中另一个构造器时即会调用父类构造器。
· 子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前隐式调用父类无参数的构造器。
1.4 多态
class BaseClass{
public int book=5;
public void base(){
System.out.println("父类的普通方法");
}
public void test(){
System.out.println("父类的被覆盖的方法");
}
}
public class SubClass extends BaseClass{
//
public String book="java book";
public void test(){
System.out.println("子类的覆盖父类的方法");
}
public void sub(){
System.out.println("子类的普通方法");
}
public static void main(String[] args){
//下面编译时类型和运行时类型不一样,发生多态
BaseClass bs=new SubClass();
System.out.println(bs.book);
bs.test();
bs.base();
/*运行结果:
5
子类的覆盖父类的方法
父类的普通方法*/
}
}
复制代码
引用变量bs比较特殊,它的编译时类型是BaseClass,而运行时类型是SubClass,当调用该引用变量的test()方法(BaseClass类 中定义了该方法,子类SubClass覆盖了父类的该方法)时,实际执行的是SubClass类中覆盖后的test()方法。这就可能出现多态了。
因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无需任何类型转换。 或者被称为向上转型,向上转型由系统自动完成。
相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。
注意:引用变量在编译阶段只能调用其编译时类型所具有的方法。但运行时则执行它运行时类型所具有的方法。因此,引用变量 只能调用声明该变量时所用类里包含的方法。例如,通过Object p=new Person()代码定义一个变量p,则这个p只能调用Object 类的方法,而不能调用Person类里定义的方法。
1.5 继承与组合
使用继承的注意点: · 尽量隐藏父类的内部数据。尽量把父类的所有成员都设置成private访问类型,不要让子类直接访问弗雷德成员变量。
· 不要让子类可以随意访问、修改父类的方法。父类中那些仅为辅助其他的工具方法,应该使用private访问控制符修饰, 让子类无法访问该方法;如果父类中的方法需要被外部类调用,则必须以public修饰,但又不希望子类重写该方法,可以 使用final修饰符来修饰该方法;如果希望父类的某个方法被子类重写,但不希望被其他类自由访问,则可以使用protected 来修饰方法。
· 尽量不要在父类构造器中调用将要被子类重写的方法。
public class ConversionTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
double d=13.4;
long l=(long)d;
System.out.println(l);
int in=5;
//boolean b=(int)in;//error
Object obj="hello";
//obj变量编译时类型是object,object和string存在继承关系,可以强制类型转换
//而且obj变量的实际类型是String,所以运行时也可通过
String objstr=(String)obj;
System.out.println(objstr);
Object objpri=new Integer(5);//objpri实际类型是Integer
if(objpri instanceof String){
String s=(String)objpri;//运行时会引发ClassCastException异常,所以先用instanceof运算符判断是否可以成功转换。
}
}
}
复制代码
instanceof运算符
instanceof运算符的前一个操作数通常是一个引用变量,后一个操作数通常是一个类(也可以是接口,可以把接口理解成一种特殊的类), 他用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回true,否则返回false。
class Base{
public Base(){
test();
}
public void test(){
System.out.println("将被子类重写的方法");
}
}
public class Sub extends Base{
private String name="123";
public void test(){
System.out.println("子类重写父类的方法"+" 其name字符串长度"+name.length());
}
public static void main(String[] args){
Sub s=new Sub();
s.name="a";
}
}
复制代码
当系统试图创建Sub对象时,同样会先执行其父类构造器,如果父类构造器调用了被其子类重写的方法,则变成调用被子类重写后 的方法。
1.6静态初始化块
如果定义初始化块时使用了static修饰符,则这个初始化块就变成静态初始化块,也称类初始化块。(普通初始化块负责对对象执行 初始化,类初始化块则负责对类进行初始化)。系统将在类初始化阶段执行静态初始化块,而不是在创建对象时才执行。因此静态 初始化块总是比普通初始化块先执行。
class root{
static{
System.out.println("root的静态初始化块");
}
{
System.out.println("root普通初始化块");
}
public root(){
System.out.println("root的无参构造器");
}
}
class Mid extends root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid普通初始化块");
}
public Mid(){
System.out.println("Mid的无参构造器");
}
public Mid(String Msg){
this();
System.out.println("Mid带参构造器,其参数值为:"+Msg);
}
}
class leaf extends Mid{
static{
System.out.println("leaf的静态初始化块");
}
{
System.out.println("leaf普通初始化块");
}
public leaf(){
super("java crazy");
System.out.println("leaf的构造器");
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
new leaf();
System.out.println();
new leaf();
}
}
复制代码