您的位置:首页 > 编程语言 > Java开发

java-基础巩固 3

2015-09-09 18:12 519 查看
基础掌握

1. static关键字的用法

简而言之:方便在没有创建对象的情况下来进行调用(方法/变量)。

 
很显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。 static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。

1)static方法

在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

另外记住,即使没有显示地声明为static,类的构造器实际上也是静态方法。

2)static变量

  static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

  static成员变量的初始化顺序按照定义的顺序进行初始化。

3)static代码块

  static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。

1.下面这段代码的输出结果是什么?



base static
test static
base constructor
test constructor


在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new
Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。

2.这段代码的输出结果是什么?


View
Code

  类似地,我们还是来想一下这段代码的具体执行过程。首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person
person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。
2. 值传递和引用传递

1:按值传递是什么

指的是在方法调用时,传递的参数是按值的拷贝传递。
按值传递重要特点:传递的是值的拷贝,也就是说传递后就互不相关了。

2:按引用传递是什么

指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。

传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

public class TempTest {

private void test1(A a){

a.age = 20;

System.out.println("test1方法中的age="+a.age);

}

public static void main(String[] args) {

TempTest t = new TempTest();

A a = new A();

a.age = 10;

t.test1(a);

System.out.println(”main方法中的age=”+a.age);

}

}

class A{

public int age = 0;

}

3. String的不变模式

一个字符串对象创建后它的值不能改变。

String str1="hello";//创建一个对象hello,不会变;

System.out.println(str1);

str1+=" world!";//两个字符串对象粘粘,系统其实创建了一个新的对象,把Str1的指向改了,指向新的对象;hello就 &nb
48cec
sp; //变成了垃圾;

System.out.println(str1);

//如果一直这样创建会影响系统的效率;要频繁的改变字符串对象的值就用StringBuffer来描述;

StringBuffer sb=new StringBuffer("[");

sb.append("hehe");

sb.append("]");//append();不会制造垃圾,真正在改sb的值;

System.out.println(sb);

4. 内部类的种类


  Java 内部类种类及使用解析


内部类Inner Class

  将相关的类组织在一起,从而降低了命名空间的混乱。

  一个内部类可以定义在另一个类里,可以定义在函数里,甚至可以作为一个表达式的一部分。

  Java中的内部类共分为四种

  静态内部类static inner class (also called nested class)

  成员内部类member inner class

  局部内部类local inner class

  匿名内部类anonymous inner class



静态内部类Static Inner Class

  最简单的内部类形式。

  类定义时加上static关键字。

  不能和外部类有相同的名字。

  被编译成一个完全独立的.class文件,名称为OuterClass$InnerClass.class的形式。

  只可以访问外部类的静态成员和静态方法,包括了私有的静态成员和方法。

  生成静态内部类对象的方式为:

  OuterClass.InnerClass inner = new OuterClass.InnerClass();

  静态内部类使用代码:



package com.learnjava.innerclass;

class StaticInner
{
private static int a = 4;

// 静态内部类
public static class Inner
{
public void test()
{
// 静态内部类可以访问外部类的静态成员
// 并且它只能访问静态的
System.out.println(a);
}

}
}

public class StaticInnerClassTest
{

public static void main(String[] args)
{
StaticInner.Inner inner = new StaticInner.Inner();
inner.test();
}
}



成员内部类Member Inner Class

  成员内部类也是定义在另一个类中,但是定义时不用static修饰。

  成员内部类和静态内部类可以类比为非静态的成员变量和静态的成员变量。

  成员内部类就像一个实例变量。

  它可以访问它的外部类的所有成员变量和方法,不管是静态的还是非静态的都可以

  在外部类里面创建成员内部类的实例:

  this.new Innerclass();

  在外部类之外创建内部类的实例:

  (new Outerclass()).new Innerclass();

  在内部类里访问外部类的成员:

  Outerclass.this.member

  详情见代码例子:



package com.learnjava.innerclass;

class MemberInner
{
private int d = 1;
private int a = 2;

// 定义一个成员内部类
public class Inner2
{
private int a = 8;

public void doSomething()
{
// 直接访问外部类对象
System.out.println(d);
System.out.println(a);// 直接访问a,则访问的是内部类里的a

// 如何访问到外部类里的a呢?
System.out.println(MemberInner.this.a);
}

}

}

public class MemberInnerClassTest
{

public static void main(String[] args)
{

// 创建成员内部类的对象
// 需要先创建外部类的实例
MemberInner.Inner2 inner = new MemberInner().new Inner2();

inner.doSomething();
}
}



局部内部类Local Inner Class

  局部内部类定义在方法中,比方法的范围还小。是内部类中最少用到的一种类型。

  像局部变量一样,不能被public, protected, private和static修饰。

  只能访问方法中定义的final类型的局部变量。

  局部内部类在方法中定义,所以只能在方法中使用,即只能在方法当中生成局部内部类的实例并且调用其方法。



package com.learnjava.innerclass;

class LocalInner
{
int a = 1;

public void doSomething()
{
int b = 2;
final int c = 3;
// 定义一个局部内部类
class Inner3
{
public void test()
{
System.out.println("Hello World");
System.out.println(a);

// 不可以访问非final的局部变量
// error: Cannot refer to a non-final variable b inside an inner
// class defined in a different method
// System.out.println(b);

// 可以访问final变量
System.out.println(c);
}
}

// 创建局部内部类的实例并调用方法
new Inner3().test();
}
}

public class LocalInnerClassTest
{
public static void main(String[] args)
{
// 创建外部类对象
LocalInner inner = new LocalInner();
// 调用外部类的方法
inner.doSomething();
}

}



匿名内部类Anonymous Inner Class

  匿名内部类就是没有名字的局部内部类,不使用关键字class, extends, implements, 没有构造方法。

  匿名内部类隐式地继承了一个父类或者实现了一个接口

  匿名内部类使用得比较多,通常是作为一个方法参数。



package com.learnjava.innerclass;

import java.util.Date;

public class AnonymouseInnerClass
{

@SuppressWarnings("deprecation")
public String getDate(Date date)
{
return date.toLocaleString();

}

public static void main(String[] args)
{
AnonymouseInnerClass test = new AnonymouseInnerClass();

// 打印日期:
String str = test.getDate(new Date());
System.out.println(str);
System.out.println("----------------");

// 使用匿名内部类
String str2 = test.getDate(new Date()
{
});// 使用了花括号,但是不填入内容,执行结果和上面的完全一致
// 生成了一个继承了Date类的子类的对象
System.out.println(str2);
System.out.println("----------------");

// 使用匿名内部类,并且重写父类中的方法
String str3 = test.getDate(new Date()
{

// 重写父类中的方法
@Override
@Deprecated
public String toLocaleString()
{
return "Hello: " + super.toLocaleString();
}

});

System.out.println(str3);
}
}


  生成的.class文件中,匿名类会生成OuterClass$1.class文件,数字根据是第几个匿名类而类推。

  Swing中使用内部类的例子如下:



package com.learnjava.innerclass;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;

public class SwingTest
{
public static void main(String[] args)
{
JFrame frame = new JFrame("JFrame");
JButton button = new JButton("JButton");

button.addActionListener(new ActionListener()
{
// new出来一个实现了ActionListener接口的类的实例

@Override
public void actionPerformed(ActionEvent arg0)
{
System.out.println("Hello World");

}
});

//加入按钮
frame.getContentPane().add(button);

//设置关闭行为
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setSize(200, 200);

frame.addWindowListener(new WindowAdapter()
{
//也可以使用继承了适配器类的匿名内部类
@Override
public void windowClosing(WindowEvent e)
{

System.out.println("Closing");
System.exit(0);
}
});
frame.setVisible(true);
}

}


5. 如何实现多继承

接口实现多继承

使用接口的核心原因是,为了能够向上转型为多个基类型。即利用接口的多实现,可向上转型为多个接口基类型。以前在学Java的时候对于接口是直接飙过去,并未尝试去深究它的意义,只是简单地记住了它是用来折衷地解决多继承问题。直到最近重新开始复习,才被这一问题弄得晕头转向的。

先来看一下为何 Java 不支持多继承,原因是多继承容易导致钻石危机(也称棱形问题),用一幅图来说明一下:





假设 类A 中有一个public方法 fun(),然后 类B 和 类C 同时继承了 类A,在 类B 或 类C 中各自对方法 fun()进行了覆盖,这时 类D 通过多继承同时继承了 类B 和 类C,这样便导致钻石危机了,程序在运行的时候对于方法 fun()该如何判断?

所以 Java 并不支持多继承,但是 Java 通过接口可以折衷地解决多继承问题,用一段代码来说明一下比较容易理解:

?
然后编写一个Checken类,暂时把它想象成父类。

?
再编写一个Orange类,同样暂时把它想象成父类。

?
最后我们通过测试类 MainTest 来理解接口是如何实现类似多继承机制的。

?
上面代码的运行结果如下:





当我们使用 HowToEat Test = new Checken(); 的时候,接口中的 howToEat() 方法采用 Checken.howToEat() 来实现,当我们使用 test = new Orange() 的时候,接口中的 howToEat() 方法采用 Orange.howToEat() 来实现,这样是不是可以想象成 HowToEat 类同时继承了 Checken 类和 Orange 类,但具体继承哪个特性则在 new 的时候再来进行决定,这样就巧妙地解决钻石危机同时又可以建立类似多继承的机制。




内部类实现

如果对于接口还是无法理解过来,当然也可以使用内部类(嵌套类)来实现类似多继承,不必担心会发生钻石危机,因为用内部类实现多继承过程中由设计者重新进行函数命名,从而避免了钻石危机。下面用代码来进行说明:

要继承的类 Father。

?
要继承的类 Mother。

?
类 Son 同时继承了 Father 和 Mother 的 output() 方法的实现。

?
测试类 MainTest。

?
测试结果图如下:





6. Final,finalize和finally的用法

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。

finally是异常处理语句结构的一部分,表示总是执行。

finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。

7. 异常的处理流程

8. 节点流和过滤流的区别

9. 字节流如何转换为字符流


Java字节流和字符流的转换器:StreamDecoder

我们的机器只会读字节码,而我们人却很难读懂字节码,所以人与机器交流过程中需要编码解码。

InputStreamReader及其子类FileReader:(从字节到字符)是个解码过程;

OutputStreamWrite及其子类FileWriter:(从字符到字节)是个编码过程。

InputStreamReader这个解码过程中,最主要的就是StreamDecoder类



InputStream到Reader的过程要指定编码字符集,否则将采用操作系统默认字符集,很可能会出现乱码问题。

10.重载和重写
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: