1,接口的使用细节
一个类可以实现多个接口, 这个是Java不直接支持多继承的一种转换形式。在java之前,c/c++支持多继承,即一个子类可以有多个父类,而java只能有一个父类,那么想使用其他类的方法就没有办法实现了。为了拟补这方面的缺陷,java提出了接口的概念,一个类可以实现多个接口。
接口A
interface A{
public abstract void methodA();
}
接口B
interface B{
public abstract void methodB();
}
类C实现A,B
class C implements A,B{
@Override
public void methodB() {
// TODO Auto-generated method stub
}
@Override
public void methodA() {
// TODO Auto-generated method stub
}
}
注意:实现A,B接口,就要全部实现他们的方法!
问题:如果A,B中有相同的方法声明,即返回值类型,方法名,参数类型一模一样,那接口该怎么处理呢?
interface D{
public abstract void show();
}
interface E{
public abstract void show();
}
class F implements D,E{
public void show(){
System.out.println("hello world");
}
}
解释:如果存在这样的情况,那么接口只要实现一个就行了,非要追究实现的具体哪一个,答案很简单,谁在前面,就是谁的。
问题:接口可以继承吗?可以,接口也支持抽象方法重写,但是当接口的抽象方法中只有返回类型不一样时,编译器会报错的!
interface G{
public abstract void show();
}
interface H extends G{
public abstract void show1();
public abstract void show();
/*
* implements G.show
`* - The return type is incompatible with G.show()
* */
//public abstract int show();
public abstract int show(int x);
}
java中抽象类和接口的使用约定:基本功能定义在抽象类中,扩展功能定义在接口中
2,多态
多态的好处和弊端:多态的好处:多态增强了程序的可扩展性。
多态的弊端:父类引用指向了子类的实例,可以访问子类重写父类的方法,但是不能调用子类特有的成员。
(即使强行转换,编译期间通过,运行期间也会抛异常的)
class Animal{
int age;
public void show(){};
/*
* 转型注意事项:
* 1,向上转型(子类转父类,编译器自动转)
* 2,向下转型(父类转子类,需要手动强制转)
* */
public static void main(String[] args) {
Animal animal = new Cat();
animal.show();
//需要强制转换
((Cat) animal).special_show();
}
}
class Cat extends Animal{
int age;
public void show(){};
public void special_show(){};
}
class Dog extends Animal{
int age;
public void show(){};
public void special_show(){};
}
注意:使用多态时,一般情况下不要用强制转换,否则会在运行时出现很多问题,就像上面代码,稍微修改以下,编译也能通过
public static void main(String[] args) {
Animal animal = new Cat();
animal.show();
((Dog) animal).special_show();
}
但是在运行时就会出错误。
3,单例模式
单例模式在软件开发中很有帮助。
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件 系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
单例模式用java实现有两种方式:懒汉式,饿汉式,特点:构造函数私有化
饿汉式:程序一加载就生成对象,不用担心线程不安全的问题,
class single{
private static final single single = new single();
private single(){}
public static single getInstance(){
return single;
}
}
懒汉式:程序第一次调用时,才生成对象,就不用在类加载时就生成对象了,面试基本上考懒汉式 ,因为懒汉式稍微写不好,就会造成线程不安全,采用了双重锁机制来避免这一问题,
class single1{
private static single1 single = null;
private single1(){}
public static single1 getInstance(){
if(null == single){
synchronized(single1.class){
if(null == single){
single = new single1();
}
}
}
return single;
}
}
4,多线程
多线程在网络系统中很重要,主要涉及到数据的安全问题,例如火车票售票系统,银行存取款系统等等。下面给出Thread的常用方法。
public static void get_threadname_and_threadObject(){
mythread t1 = new leanjava().new mythread();
//获取线程的名称
String name = t1.getName();
t1.start();
//获取当前执行的线程对象,如果Thread.currentThread()在run()方法中则返回this对象
Thread tcurrent = Thread.currentThread();
//设置线程的名称
mythread t2 = new leanjava().new mythread("mythread");
//也可以用set方法设置
t2.setName("mythread");
t2.start();
}
下面给出mythread线程类,new leanjava().new mythread();这句话说明线程类在leanjava的内部,是个内部线程类。
class mythread extends Thread{
public mythread(String string) {
super(string);
}
public mythread(){
}
public void run() {
System.out.println(Thread.currentThread().getName()+":: run......");
}
}
验证结果:
线程的状态切换:
public static void thread_status() throws InterruptedException{
//t1被创建,
mythread t1 = new leanjava().new mythread();
//t1,调用start()后,状态可以变为
// 1,运行状态(具有cpu执行权)
// 2,就绪状态(具有cpu的执行资格)
t1.start();
//t1被挂起,调用sleep()后,状态可以变为
// 1,等待状态
t1.sleep(400);
//t1.sleep()时间到或者被notice()
// 1,就绪状态
// 2,执行状态
t1.notify();
//t1 被stop()或者执行完成所有任务
// 1,消亡状态
t1.stop();
}
实现线程的两种方法:
a,继承Thread类,一般情况下重写run()方法,实例化后,调用start()方法
b,实现Runnable接口,必须实现run()方法,实例化后,用new Thread(实例化对象).start()方法
继承Thread类:
class mythread extends Thread{
public mythread(String string) {
super(string);
}
public mythread(){
}
public void run() {
System.out.println("mythread run......");
}
}
实现Runnable接口:
class mythread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("mythread2 run......");
}
}
5,run()方法和start()的区别
* 如果主线程直接调用线程的run()方法,则进程还是处于单线程状态,
* 和普通调用方法没有区别
* 如果主线程调用线程的start()方法,则start中的本地native方法start0()方法会调用
* 操作系统的底层的资源创建相关的线程。这个时候,这个的进程才是多个线程来执行
用代码来验证:先写调用函数
public static void run_start_difference(){
mythread t1 = new leanjava().new mythread();
t1.start();
System.out.println("main thread run......");
t1 = new leanjava().new mythread();
t1.run();
System.out.println("main thread run......");
}
下面是线程类代码:(加上一些延时操作)
class mythread extends Thread{
public void run() {
try {
Thread.sleep(300);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("mythread run......");
}
}
结果验证:
6,方法中的内部类
在方法中定义内部类,但是有局限性,声明内部类之前,不能进行实例化,必须在内部类的后面才能实例化。否则编译器会误认为没有这个类,如果只是声明了类,而不去使用,是没有任何意义的。
public static void thread_method(){
class myinnerthread extends Thread{
public void run() {
System.out.println("myinnerThread run......");
}
}
myinnerthread t3 = new myinnerthread();
t3.start();
}
验证结果: