JavaDay23 内部类和线程


tags:

  • 内部类
  • 线程
  • 进程
    style: summer

JavaDay23 内部类和线程

@toc

一、复习

(一)Comparable 和 Comparator

两者都是接口

  • Comparable 是一个接口,给自定义类提供比较方式,遵从 Comparable的实现类,必须实现 compareTo(T o)方法;即进行两个对象的比较;
  • Comparator 是一个接口,用来做自定义比较器,遵从 Comparator接口的实现类,必须使用 compare(T o1, T o2);

方法1.实现自定义比较器

class MyCompare implements Comparator<Student>{
  @override
  public int compare(Student o1, Student o2){
    return o1.getAge() - o2.getAge();
  }
}

上面这种方式:好理解但是需要重新定义一个类,而且可能会统一放在一个包里面,略显复杂;

方法 2:使用匿名内部类的匿名对象
格式:Arrays.sort(T[] t, Comparator<? extends T> com)

Student[] array = {stu1, stu2, stu3};

Arrays.sort(array.new Comparator<Student>{
  @Override
  public int compare(Student o1, Student o2){
    return o1.getAge() - o2.getAge();
  }
});

二、内部类

  • 生活实例:
    在人类中,有些东西,比如内脏,用成员方法或者成员变量描述都显着有点不太合适,因为这些内脏首先是属于【人体的一部分】,而且会使用【人体的一些属性】,但是又拥有自己的一些【特征】;能不能把这些器官,认为是一个类,一个属于人类内部的一个类。

  • 内部类分类:
    1.成员内部类
    2.局部内部类
    3.匿名内部类

(一)成员内部类

  • 内部类和外部类比较:
    • 1.成员内部类可以使用外部类的所有成员变量和成员方法, 不管用什么权限修饰,不管是private还是public都可以使用,因为,这是在内部类的内部使用。
    • 2.【在Outer类的外部创建Outer的Inner对象】
      格式如下:
      外部类名.内部类名 内部类对象名 = new 外部类名().new 内部类名();
      例如:Outer.inner inner = new Outer().new Inner();
      【第一个知识点】:普通的成员变量和成员方法,在没有对象的情况下,不能再类外使用
    • 3.如果内部类和外部类存在同名的成员变量,这里默认是就近原则,使用的是内部类的成员变量
      如果想要使用外部类的成员变量和成员方法的格式:
      外部类名.this.同名成员变量;
      外部类名.this.同名成员方法(参数列表);
    • 4.在外部类的类内方法中,可以创建内部类的对象
package com.qfedu.a_innnerclass;

class Outer {
	int num = 100; //外部类的成员变量

	private static int s = 10;

	class Inner { //这是Outer类的一个成员内部类
		int i = 10; //内部类的成员变量
		int num = 50;
		public void testInner() {
			System.out.println("内部类的成员方法");
			testOuter();
			System.out.println("内部类同名成员变量:" + num); // 50 就近原则
			//使用  外部类名.this.成员变量名  获取同名外部成员变量
			System.out.println("外部类同名成员变量:" + Outer.this.num);
			
			//分别调用内部和外部成员方法
			test();
			Outer.this.test();

			System.out.println(s);
			testStatic();
		}

		public void test() {
			System.out.println("内部类的test方法");
		}
	}//inner

	
	public void testOuter() {
		System.out.println("外部类的成员方法");
	}

	public void test() {
		System.out.println("外部类的test方法");
	}

	//为了将内部类的语句输出
	//在【外部类】中定义【内部类的类对象】
	public void createInnnerClass() {
		//外部类的成员方法中调用内部类的构造方法,通过new关键字来创建内部类类对象使用
		Inner inner = new Inner();
		inner.testInner();
	}

	public static void testStatic() {
		System.out.println("外部类的静态成员方法~");
	}
}

//☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
public class Demo1 {
	public static void main(String[] args) {
		//创建内部类对象的方法一:在外部类中创建内部类对象
//		Outer outer = new Outer();
//		outer.createInnnerClass();

		//创建内部类对象的方法一:在类外创建一个Inner类对象
		//数据类型是Outer.Inner 表示是外部类里面的内部类数据类型
		Outer.Inner inner = new Outer().new Inner();
		inner.testInner();
	}
}

运行结果:

内部类的成员方法
外部类的成员方法
内部类成员变量:50
外部类成员变量:100
内部类的test方法
外部类的test方法
10
外部类的静态成员方法~

(二)局部内部类

放在方法内或者函数内的类,称之为局部内部类

  • 【第二个知识点】

    • 1.局部变量的
      • 【生存期】
        从声明定义位置开始,到代码块结束
      • 【作用域】
        只能在当前代码块内部
    • 2.类对象的
      • 【生存期】
        通过new关键字创建的时候开始,JVM垃圾回收机制调用时,销毁该对象,结束
      • 【作用域】
        那个引用变量拥有这个对象的首地址,哪里就是他的作用域
  • 说明:
    局部内部类只能在函数或者方法中使用,方法或者函数外不能使用

  • 发现:
    局部内部类中,貌似不能【修改】所在方法或者函数中的局部变量,原因如下:

局部内部类的对象是在函数或者说方法的内部通过JVM借助于new关键字,和局部内部类的构造方法,创建的一个类对象,并且该对象是由JVM的垃圾回收机制回收的;
但是局部变量n是在testInner()方法中,而这个局部变量的n的生存周期是和testInner()该方法的大括号有关,生存期和作用域都是在大括号以内;
如果在testInner()方法的内部,MethodInner()这个类是方法中的局部内部类,而创建的对象在使用testInner()方法中的局部变量时,因为对象的销毁时间不确定,但是一定是晚于局部变量的销毁的,这里隐含了一个类对象【延长了局部变量生存期】,这个是不符合Java原理的;
这就是为什么不能修改,因为有可能在这个对象被使用的时候,局部变量的内存空间已经被内存收回,换而言之这个局部变量不存在了。

  • 【解决方法】
    如果是在局部内部类中使用的所在函数或者方法的局部变量,该变量用final修饰
package com.qfedu.a_innnerclass;

class Test {
	int num = 100;
	
	public void testInner() {
		//这里是在方法中定义了一个类,这个类只能在当前函数或者方法中使用
		final int n = 10;
		class MethodInner {
			int i = 10;
			
			public void function() {
				System.out.println(i);
				num = 20;
				System.out.println(num);
				//n = 20;//使用final之后就可以了
				System.out.println(n);
				System.out.println("局部内部类的成员方法");
			}
		}
		
		MethodInner methodInner = new MethodInner();
		methodInner.function();
	}
	
}

public class Demo2 {
	public static void main(String[] args) {
//		for (int i = 0; i < 10; i++) {
//			System.out.println(i);
//		}
		
//		System.out.println(i);
		new Test().testInner();
	}
}

程序运行结果:

10
20
10
局部内部类的成员方法

(三)匿名内部类

只是没有了名字,但是类本体中该实现的还得实现;
匿名内部类必须继承一个类或者实现一个接口
【第四个知识点】
类的本体:在类声明部分,大括号里面的内容就是类 的本体!!!

class Test {
	//成员变量
	//成员方法
}

匿名内部类就是没有名字的类,但是有类的本体!!!

package com.qfedu.a_innnerclass;

import java.util.Arrays;
import java.util.Comparator;

abstract class Animal {
	int age;

	public abstract void test();
	abstract public void jump();
}

class CodingMonkey extends Animal {

	//以下两个方法是类的本体!!!
	@Override
	public void test() {
		System.out.println("这是类本体中的一个成员方法");
	}

	@Override
	public void jump() {
		System.out.println("Hello");
	}
}



interface A {
	int i = 0; // public static final
	public void testA(); //abstract
}

public class Demo3 {
	public static void main(String[] args) {
		//方法一:正常方法: 可以创建CodingMonkey即Animal的子类的对象,进行方法的操作
		CodingMonkey cm = new CodingMonkey();

		//Animal a = cm; 多态,父类的引用指向子类的对象~
		cm.test();
		cm.jump();


		//方法二:匿名对象类: 也可以直接通过匿名对象类进行直接操作

     /*
		Animal是一个抽象类,ani 是抽象类的一个引用类型变量

		new Animal() {
			发现这里面的内容和继承该抽象类的子类内容一致,都是抽象类,要求子类实现的方法;同时这里是类的本体,而这里可以看做是一个类,但是这个类没有名字,所以:这个就是【匿名内部类】;本质上这里创建了一个【匿名内部类】的对象,赋值给了Animal的引用数据类型 ani,同时这里隐含了一个【继承】关系
			 };
			 */
		Animal ani = new Animal() {
			//同样需要实现继承的相关功能
			@Override
			public void test() {
				System.out.println("匿名内部类的里面的test方法");
			}

			@Override
			public void jump() {
				System.out.println("匿名内部类的Hello");
			}
		};

		ani.jump();
		ani.test();


		//以上更加简洁的使用方式:

		//匿名内部类的匿名对象直接调用方法
		new A() {
			//这里是一个类【遵从】接口之后,要求实现接口中的抽象方法,这里也是一个【匿名内部类】
			@Override
			public void testA() {
				System.out.println("匿名内部类实现interface A中的testA方法");
			}
		}.testA();//new A(){}可以看成匿名对象
		//Java Android开发

		Integer[] arr = {3, 2, 4, 5, 6, 1};

		//匿名内部类的匿名对象作为方法的参数
		Arrays.sort(arr, new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				return o1 - o2;
			}
		});

		System.out.println(Arrays.toString(arr));
	}

}

程序运行结果:

这是类本体中的一个成员方法
Hello
匿名内部类的Hello
匿名内部类的里面的test方法
匿名内部类实现interface A中的testA方法
[1, 2, 3, 4, 5, 6]
  • 在使用匿名内部类的过程中,我们需要注意如下几点:
    1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
    2、匿名内部类中是不能定义构造函数的
    3、匿名内部类中不能存在任何的静态成员变量和静态方法
    4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
    5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

三、多线程与多进程

  • 多线程和多进程:

    • 线程:
      在一个软件中,负责不同功能的子程序,称之为线程。
    • 进程:
      是在计算机系统中所有正在运行的程序,都可以看做是一个进程。多进程的操作系统
  • 从实际出发:
    每一个CPU的内核在一个时间片内只能执行一件事情。但是这个时间片非常的短,不同的程序会出现在不同的时间片上,不断的切换,所以你感觉是在同时运行,现在的CPU基本上都是多核多线程的。

  • 面试题:
    请问一个Java程序再运行的时候,最少有几个线程???
    最少是两个线程:1. main线程,即主线程 2. JVM的垃圾回收机制

  • 多线程的好处
    1.可以让一个程序同时执行不同的任务
    2.可以提高资源的利用率

  • 多线程的弊端
    1.线程安全问题 之前的迭代器
    2.增加了CPU负担
    3.降低了其他程序CPU的执行概率
    4.容易出现死锁行为

  • 如何创建一个线程
    两种方式:
    1.自定义一个类继承Thread类,那么这个类就可以看做是一个多线程类
    2.要求【重写】Thread类里面的run方法,把需要执行的自定义线程代码放入这个run方法

下面是使用迭代器出现的同时操作问题;

package com.qfedu.b_thread;

import java.util.ArrayList;
import java.util.Iterator;

class MyThread extends Thread {
	//要求重写【run】方法
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("自定义Thread类:" + i);
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		
		//1. 创建自定义线程类的类对象
		MyThread mt = new MyThread();
		
		//2. 启动线程 。
		//mt.run();尝试调用n方法 相当于调用了一个普通的方法,而不是一个线程
		mt.start(); //启动线程的方式,会发现,MyThread线程和main线程在抢占CPU资源
		
		for (int i = 0; i < 10; i++) {
			System.out.println("main线程:" + i);
		}




		/*
		ArrayList<String> list = new ArrayList<String>();
		
		list.add("hello, how do you do");
		list.add("I'm fine, Thank you , and you");
		list.add("I'm fine, too");

        //三种遍历方式:
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
		
		for (String string : list) {
			System.out.println(string);
		}
		
		Iterator<String> it = list.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
		*/
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值