黑马程序员_内部类

----------------------------------------------------------------------------- android培训java培训、期待与您交流! --------------------------------------------------------------------------------

 

什么是内部类?

大部分时候我们将类定义成一个独立的程序单元;在某些情况下,我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有的也叫嵌套类),包含内部类的类也被称为外部类(有的也叫宿主类)。

 

 

内部类主要有如下作用:

  1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
  1. 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问;但外部类不能访问内部类的实现细节,例如内部类的属性。
  1. 匿名内部类适合用于创建那些仅需要一次使用的类。

 

非静态内部类

定义内部类非常简单,只要把一个类放在另一个类内部定义即可。此处的“类内部”包括类中的任何位置,甚至在方法中也可以定义内部类(方法里定义的内部类被称为局部内部类)。

大部分时候内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类时一种与属性、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。

成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类时静态内部类,没有使用static修饰的成员内部类时非静态内部类。

 

因为内部类作为其外部类的成员,因此可以使用任意访问控制符如:privateprotectedpublic等修饰。

 

注意:

外部类的上一级程序单元是包,所以它只有2个作用域:同一个包和任何位置。因此只需两种访问权限:包访问权限和公开访问权限。正好对应省略访问控制符和public访问控制符。省略访问控制符是包访问权限,即同一包中的其他类可访问省略访问控制符的成员。因此,如果一个外部类不适用任何访问控制符修饰,则只能被同一个包中其他类访问。而内部类的上一级程序单元是外部类,它就具有四个作用域:同一个类、同一个包、父子类和任何位置,因此可以使用四种访问控制权限。

 

内部类的访问规则:

  1. 内部类可以直接访问外部类中的成员,包括私有;

之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式: 外部类名.this

  1. 外部类要访问内部类,必须建立内部类对象。

 

访问格式:

  1. 当内部类定义在外部类的成员位置上,而且非私有,可以在外面其他类中。

            可以直接建立内部类对象。

格式:

外部类名.内部类名  变量 = 外部类对象.内部类对象;

Outer.Inner in = newOuter().new Inner();

 

  1. 当内部类在成员位置上,就可以被成员修饰符所修饰。

           比如,private:将内部类在外部类中进行封装。

static:内部类就具备了static的特性。

当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。

 

在外部其他类中,如何直接访问static内部类的非静态成员呢?

newOuter.Inner().function();

 

在外部其他类中,如何直接访问static内部类的静态成员呢?

Outer.Inner.function();

 

class Outer
{
	private int x = 3;

	
	class Inner//内部类
	{
		//int x = 4;
		void function()
		{
			//int x = 6;
			System.out.println("innner :"+Outer.this.x);
		}
	}

	/**/
	void method()
	{
		Inner in = new Inner();
		in.function();
	}
}


class  InnerClassDemo
{
	public static void main(String[] args) 
	{
		Outer out = new Outer();
		out.method();

		//直接访问内部类中的成员。
//		Outer.Inner in = new Outer().new Inner();
//		in.function();
	}
}


注意:

当内部类中定义了静态成员,该内部类必须是static的。

当外部类中的静态方法访问内部类时,内部类也必须是static的。

 

内部类定义在局部时,

  1. 不可以被成员修饰符修饰;
  1. 可以直接放那个吻外部类中的成员,因为还持有外部类中的引用。

但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。

 

匿名内部类:

  1. 匿名内部类其实就是内部类的简写格式。
  1. 定义匿名内部类的前提:

内部类必须继承一个类或者实现接口;

  1. 匿名内部类的格式: new父类或者接口(){定义子类的内容}
  1. 其实匿名内部类就是一个匿名子类对象,而且这个对象有点胖,也可以理解为带内容的对象。
  1. 匿名内部类中定义的方法最好不要超过3个。(最好是两个或是一个)
	interface Inter
	{
		void show(int a, int b);
		void func();
	}
	
	class Demo
	{
		public static void main(String[] args)
		{
			//匿名内部类
			Inter in = new Inter()
			{
				public void show(int a, int b)
				{
				
				}
				public void func()
				{
				
				}
			};
			in.show(4,5);
			in.func();
		}
	}

 


异常

就是程序在运行时出现不正常情况。

异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述。并封装成对象。其实就是java对不正常情况进行描述后的对象体现。

 

对于问题的划分:两种:一种是严重的问题,一种非严重的问题。

对于严重的,java通过Error类进行描述;

对于Error一般不编写针对性的代码对其进行处理。

对于非严重的,java通过Exception类进行描述。

对于Exception可以使用针对性的处理方式进行处理。

 

2.异常的处理

java提供了特有的语句进行处理。

处理异常的基本代码块格式:

try

{

需要被检测的代码;

}

catch (异常类 变量)

{

处理异常的代码:(处理方式)

}

finally

{

一定会执行的语句:

}

 

3,对捕获到的异常对象进行常见方法操作。

String getMessage():获取异常信息

 

其实jvm默认的异常处理机制,就是在调用printStackTrace方法。打印异常的堆栈中的跟踪信息。

<<ExceptionDemo.java>>

 

异常声明throws

在功能上通过throws的关键字声明了该功能有可能会出现问题。

 

对多异常的处理:

  1. 声明异常时,建议声明更为具体的异常。这样处理的可以更具体。
  2. 对方声明几个异常,就对应有几个catch块。不要定义多余的catch块。

如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。

 

建立在进行catch处理时,catch中一定要定义具体处理方式。

不要简单定义一句e.printStackTrace(),也不要简单的就书写一条输出语句。

 

自定义异常

因为项目中会出现特有的问题

而这些问题并未被java所描述并封装对象。

所以对于这些特有的问题可以按照java的对问题封装的思想。

将特有的问题,进行自定义的异常封装。

class Demo
{
	int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException//在功能上通过throws的关键字声明了该功能有可能会出现问题。
	{

		int[] arr = new int[a];

		System.out.println(arr[4]);

		return a/b;
	}
}


class  ExceptionDemo2
{
	public static void main(String[] args) //throws Exception
	{
		Demo d = new Demo();
		try
		{
			int x = d.div(5,0);
			System.out.println("x="+x);
		}
		
		catch(Exception e)
		{
			System.out.println("hahah:"+e.toString());
		}
		catch (ArithmeticException e)
		{
			System.out.println(e.toString());
			System.out.println("被零除了!!");

		}
		catch (ArrayIndexOutOfBoundsException e)
		{
			System.out.println(e.toString());
			System.out.println("角标越界啦!!");
		}
		
		
		

		System.out.println("over");

	}
}

 


throwthrows的区别

throws使用在函数上;(写在小括号和大括号之间)

throw使用在函数内;(后面跟的是异常现象)

 

throws后面跟的是异常类,可以跟多个,用逗号隔开。

throw后面跟的是异常对象。

 

RuntimeException

Exception中有一个特殊的子类异常RuntimeException;

如果在函数内抛出该异常,函数上可以不用声明,编译一样通过。

如果再函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

之所以不用在函数声明,是因为不需要让调用者处理;

当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望停止程序后;

对代码进行修正。

 

自定义异常时,如果该异常的发生无法再继续进行运算,就让自定义异常继承RuntimeException。

对于异常分两种:

1. 编译时被检测的异常。

2.编译时不被检测的异常(运行时异常,RuntimeException以及其子类)

*/

class FuShuException extends RuntimeException
{
	FuShuException(String msg)
	{
		super(msg);
	}
}
class Demo
{
	int div(int a,int b)throws Exception//throws ArithmeticException
	{
		
		if(b<0)
			throw new Exception("出现了除数为负数了");
		if(b==0)
			throw new ArithmeticException("被零除啦");
		return a/b;
	}
}

class ExceptionDemo4 
{
	public static void main(String[] args) 
	{
		
		Demo d = new Demo();
		
		int x = d.div(4,-9);
		System.out.println("x="+x);		
		
		System.out.println("over");
	}
}

/*
class Person
{
	public void checkName(String name)
	{
		
		//if(name.equals("lisi"))//NullPointerException
		if("lisi".equals(name))//if(name!=null && name.equals("lisi"))
			System.out.println("YES");
		else
			System.out.println("no");
	}
}

main()
{
	Person p = new Person();
	p.checkName(null);
}
*/


 

记住:

catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常时检测时异常,那么必须声明。

 

异常在子父类覆盖中的体现;

1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者异常的子类。

2.如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。

3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。

如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。

 

----------------------------------------------------------------------------- android培训java培训、期待与您交流!--------------------------------------------------------------------------------
详细请查看:http://edu.youkuaiyun.com/heima/

<think>好的,我现在需要详细解释JVM中的CAS机制、原子类以及它们的使用场景。首先,用户提到了CAS机制和原子类,这两个都是并发编程中的关键概念。我应该从基础开始,逐步展开,确保结构清晰,同时符合用户给出的格式要求。 首先,CAS(Compare And Swap)机制是JVM中用于实现无锁并发操作的一种技术。我需要解释它的工作原理,包括比较和交换的步骤。这里需要提到CAS涉及到的三个操作数:内存地址、期望值和新值。当内存地址的值与期望值匹配时,才会更新为新值,否则不进行操作。这部分需要结合例子来说明,比如AtomicInteger的incrementAndGet方法是如何利用CAS实现的。 接下来是CAS的优缺点。优点包括减少线程阻塞,提高并发性能;缺点则是ABA问题、循环时间长带来的开销以及只能保证单个变量的原子性。需要引用用户提供的引用[3]中的ABA问题,说明这种情况可能带来的风险,比如在并发环境下,一个值从A变成B又变回A,CAS会误认为没有变化。这时候可能需要使用版本号或者AtomicStampedReference来解决。 然后是原子类部分。Javajava.util.concurrent.atomic包提供了多种原子类,比如AtomicInteger、AtomicLong、AtomicReference等。需要分别介绍这些类的用途,并举例说明它们的使用方法。例如,AtomicInteger的getAndIncrement方法是线程安全的,无需加锁。这里可以结合用户提到的引用[1],说明JVM如何通过本地方法(native方法)实现这些原子操作,底层可能依赖C/C++或硬件指令。 使用场景方面,需要明确在哪些情况下适合使用CAS和原子类。例如,计数器、状态标志的更新、构建无锁数据结构等。同时,要指出不适合的场景,比如涉及多个共享变量的复杂操作,这时候可能需要使用锁或其他同步机制。 最后,生成相关问题,确保问题覆盖了用户可能感兴趣的扩展内容,比如CAS的ABA问题解决方案、原子类与synchronized的比较、在分布式系统中的应用等。这些问题需要与之前的讲解内容相关联,帮助用户进一步学习。 需要特别注意格式要求:行内数学公式用$...$,独立公式用$$...$$,但当前内容可能不涉及数学公式,所以这部分可能不需要。但如果有相关公式,比如涉及到算法的时间复杂度,可能需要用LaTeX表示。不过根据用户的问题,可能暂时不需要数学表达式,但需要保持中文回答,正确引用标记,如[^3]对应引用中的CAS ABA问题。 总结步骤: 1. 解释CAS机制原理,优缺点,引用相关引用。 2. 介绍原子类的种类和用法,结合引用[1]中的native方法说明。 3. 分析使用场景,适合和不适用的案例。 4. 生成相关问题,覆盖机制、问题、比较和应用扩展。 需要确保语言简洁,结构清晰,分点说明,避免技术术语过于晦涩,让不同层次的读者都能理解。同时检查引用是否正确对应,比如引用[3]对应ABA问题,引用[1]对应native方法的实现。</think>### JVM中的CAS机制与原子类详解 #### 一、CAS机制原理 CAS(Compare And Swap)是一种无锁并发控制技术,通过**比较并交换**实现线程安全操作。其核心步骤如下: 1. 读取内存地址的当前值$V$。 2. 比较$V$与期望值$A$。 3. 若相等,将内存值更新为新值$B$;否则不操作并重试。 示例:`AtomicInteger`的`incrementAndGet()`方法通过循环CAS实现原子递增: ```java public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } ``` #### 二、CAS的优缺点 - **优点**:避免线程阻塞,提升并发性能。 - **缺点**: 1. **ABA问题**:值从$A→B→A$的变化可能导致CAS误判(可通过`AtomicStampedReference`添加版本号解决)。 2. **循环开销**:高竞争下频繁重试可能降低性能。 3. **单变量限制**:仅支持单个变量的原子操作。 #### 三、原子类的分类与使用 Java提供`java.util.concurrent.atomic`包,包含以下原子类: 1. **基本类型**: - `AtomicInteger`:原子整型操作。 - `AtomicLong`:原子长整型操作。 2. **引用类型**: - `AtomicReference`:原子更新对象引用。 - `AtomicStampedReference`:带版本号的引用,解决ABA问题[^3]。 3. **数组类型**: - `AtomicIntegerArray`:原子整型数组操作。 示例:使用`AtomicInteger`实现计数器: ```java AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 线程安全递增 ``` #### 四、使用场景 1. **适用场景**: - **计数器**:如统计请求次数。 - **状态标志**:如线程启停控制。 - **无锁数据结构**:如`ConcurrentLinkedQueue`(基于CAS的无锁队列)。 2. **不适用场景**: - **多变量原子操作**:需使用锁或`AtomicReference`封装。 - **复杂业务逻辑**:高竞争时可能导致性能下降。 #### 五、底层实现 原子类的CAS操作依赖JVM通过**native方法**调用底层CPU指令(如x86的`CMPXCHG`)实现[^1]。例如,`compareAndSet()`方法在JVM内部通过C/C++代码直接操作内存。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值