
Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类。内部类又分为:常规内部类、局部内部类、匿名内部类和静态嵌套类四种。 常规/局部内部类 包含在一个类的类体中的类。 扩展:外部类的概念:包含了内部类的类。 问:在内部类的方法中,是否可以直接访问外部类的成员? 答: 肯定的。 问:在外部类的方法中,是否可以直接访问内部类的成员? 案: 否定的。 问: 那,如何访问? 答: 先创建内部类的对象,再通过对象去访问。 Demo <span style="font-size:18px;"><span style="font-size:18px;">public class InnerClassTest { publicstatic void main(String[] args) { //InnerClass iObj = new InnerClass(); } } class OutClass { //外部类 privateString info; //外部类的实例变量 publicOutClass() { } publicOutClass(String info) { this.info= info; } publicvoid setInfo(String info) { this.info= info; } publicString getInfo() { returninfo; } //外部类的功能 publicvoid showInfo(){ System.out.println(info ); //System.out.println("在外部类的方法中直接访问内部类的成员x = " + x ); InnerClassiObj = new InnerClass(10, 20); System.out.println("在外部类的方法中必须通过内部类的对象,才能访问其成员. 如: iObj.x = " + iObj.x ); } publicclass InnerClass{ //内部类 privateint x; privateint y; publicInnerClass() { } publicInnerClass(int x, int y) { this.x= x; this.y= y; } publicvoid setX(int x) { this.x= x; } publicvoid setY(int y) { this.y= y; } publicint getX() { returnx; } publicint getY() { returny; } //内部类的功能方法 publicvoid showXY(){ System.out.println("x = " + x + ", y = " + y ); System.out.println("在内部类的方法中直接访问外部类的成员 info = " + info ); } } }</span></span> 匿名内部类 没有名称的内部类就是匿名的内部类。 从代码形式上看,什么样的类为匿名的内部类? 在创建一个类的对象时,在其构造器的末尾添加一对大括号,在大括号中重写某个方法或某几个方法。 这一段代码构成的整体就是一个匿名的内部类。且这个匿名的内部类属于该类的子类。 Demo <span style="font-size:18px;"><span style="font-size:18px;">package com.hqg.oop.d39; import com.hqg.oop.d38.a2.MyDate; public class InnerClassTest { publicstatic void main(String[] args) { MyDatemd = new MyDate(2016, 3, 9 ){ //匿名的内部类。 @Override publicint getMonth() { if(super.getMonth() < 10 ){ System.out.print("0"); } returnsuper.getMonth(); } @Override publicint getDay() { if(super.getDay() < 10 ){ System.out.print("0"); } returnsuper.getDay(); } }; System.out.print("md对象表示的日期是:"); intyear = md.getYear(); //intmonth = md.getMonth(); //intday = md.getDay(); System.out.print(year + "年" ); System.out.print(md.getMonth()+ "月" ); System.out.println(md.getDay() + "日。" ); AppDemo ad = new AppDemo(3.36){ //匿名的内部类 @Override publicdouble area() { System.out.print("已知圆的半径 r =" + this.getR() + " 和圆周率 PI = " + PI + ";其面积area = "); returnsuper.area(); } }; doublea1 = ad.area(); System.out.println(a1 ); ADemoobj = new ADemo(){ //匿名的内部类 doubler = 3.5 ; @Override publicint add(int x, int y) { System.out.print(x + " + " + y + " = " ); returnx + y; } @Override publicdouble area() { System.out.print("\n===>>>已知圆的半径 r =" + r + " 和圆周率 PI = " + PI + ";其面积area = "); returnr * r * PI ; } }; a1= obj.area(); System.out.println(a1 ); } } //接口是否可以创建对象(实例化)? 答案: 不可以。 //(关于接口部分的内容,会在后面的总结过程中介绍,这里先不讲解) interface ADemo { publicstatic final double PI = 3.14; publicabstract int add( int x, int y ); publicabstract double area(); } class AppDemo implements ADemo { privatedouble r ; publicAppDemo() { //TODO Auto-generated constructor stub } publicAppDemo(double r) { this.r= r; } publicdouble getR() { returnr; } publicvoid setR(double r) { this.r= r; } @Override publicint add(int x, int y) { returnx + y; } @Override publicdouble area() { returnr * r * PI ; } }</span></span> 静态内部类 1、静态内部类定义 静态内部类,定义在类中,任何方法外,用static定义;静态内部类只能访问外部类的静态成员。 生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in=new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。可以定义私有静态内部类。 2、java实现静态内部类注意事项 将某个内部类定义为静态类,跟将其他类定义为静态类的方法基本相同,引用规则也基本一致。不过其细节方面仍然有很大的不同。具体来说,主要有如下几个地方要引起注意。 1)一般情况下,如果一个内部类不是被定义成静态内部类,那么在定义成员变量或者成员方法的时候,是不能够被定义成静态成员变量与静态成员方法的。也就是说,在非静态内部类中不可以声明静态成员 2)一般非静态外部类可以随意访问其外部类的成员变量以及方法(包括声明为private的方法),但是如果一个内部类被声明为static,则其在访问包括自身的外部类会有诸多的限制。静态内部类不能访问其外部类的非静态成员变量和方法 3)在一个类中创建非静态成员内部类的时候,有一个强制性的规定,即内部类的实例一定要绑定在外部类的实例中。然后要在一个外部类中定义一个静态的内部类,不需要利用关键字new来创建内部类的实例。即在创建静态类内部对象时,不需要其外部类的对象 Demo <span style="font-size:18px;"><span style="font-size:18px;">java在实现LinkedList时使用了如下内部类: public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>,Cloneable, java.io.Serializable { ........ private static class Entry<E> { E element; Entry<E> next; Entry<E> previous; Entry(E element, Entry<E> next,Entry<E> previous) { this.element = element; this.next = next; this.previous = previous; } } private Entry<E> addBefore(E e,Entry<E> entry) { Entry<E> newEntry = newEntry<E>(e, entry, entry.previous); newEntry.previous.next = newEntry; newEntry.next.previous = newEntry; size++; modCount++; return newEntry; } ........ }</span></span> 这里即静态内部类的典型用法 业务思想 在工作中,内部类的使用很方便,而最常见的尤为是匿名的内部类。当大家分工合作,敲着不同的模块,需要调用同事的类时,我们经常使用匿名的内部类,以更为高效地搞定。 合作双赢,思想无界。内部类的使用,使得我们工作起来更为轻松,也颇为舒心。
关于final关键字的总结,是很容易学习和掌握的,大致分为用final修饰的符号常量、类、方法、属性四部分,而这四部分的内容也是我们必须掌握的,很实用,在项目中经常来利用final关键字来帮忙以完成我们特定的任务。下面就来看看最终的final: 符号常量 用 final 修饰的变量就称之为最终变量,也称之为符号常量。 例如: double PI = 3.14; ===>>> final double PI = 3.14; //符号常量 Demo <span style="font-size:18px;"><span style="font-size:18px;">package com.hqg.oop.d37.a3; public class finalTest { public static void main(String[] args) { final double PI = 3.14; //PI = 3.1415926; //符号常量不可中途改变其值。 SonDemo1 sd = new SonDemo1(); int re = sd.add(10, 20); System.out.println( re ); } } class Person{ private String name; private final boolean sex ; //最终属性(只读属性) public Person( String name, boolean sex ) { this.name = name; this.sex = sex; } } class DemoA { public final int add( int x, int y ){ return x + y; } } class SonDemo1 extends DemoA { } //class Son extends String { // //} final class Father { } //class Son extends Father { // //}</span></span> 类 用 final 修饰的类,即为最终类。 特性: 它不可有子类。即: 防继承的。 例如: 专家提供的 String 类就量个最终类。 方法 用 final 修饰的方法,即为最终方法。 特性: 它不可被重写。即: 防重写。 属性 用final修饰的实例变量就是最终属性,也成为只读属性。 特性: 1) 它必须由final修饰。 2) 它一旦赋值,则终身不变。(中途不变) 3) 当声明最终的实例变量时,若没有赋值,则在每一个构造器中必须对它赋值。 4) 最终的实例变量没有设定器。 5) 最终的实例变量名称大写。 Demo <span style="font-size:18px;"><span style="font-size:18px;">package com.hqg.oop.d38.a1; public class FinalObjectVarTest { public static void main(String[] args) { Person p1 = new Person("张灵",false, 18); Person p2 = new Person("张灵",false, 18); System.out.println( p1 ); System.out.println( p2 ); System.out.println( p1.equals(p2) ); //p1.SEX = true; } } class Person { //实例变量 private String name; public final boolean SEX; //约定: true为男, false为女 private int age; public Person() { int n = (int)(2 * Math.random()); this.SEX = n == 1 ; } public Person(String name,int age) { this(); this.name = name; this.age = age; } public Person(String name, boolean sex, int age) { this.name = name; SEX = sex; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } //访问器 public String getName() { return name; } public boolean isSEX() { return SEX; } public int getAge() { return age; } // @Override public String toString() { return "姓名: " + name + " 性别: " + (SEX ? "男":"女") + " 年龄: " + age; } @Override public boolean equals(Object obj) { Person pp = (Person)obj; if( this.name.equals( pp.name ) && this.SEX == pp.SEX && this.age == pp.age ){ return true; }else{ return false; } } } </span></span> 业务思想 就像英语中的一句谚语:”Each coin has two sides”(每个硬币都有两面),关键字final正如那只硬币。用final来定义符号常量、类、方法、属性可以减少我们很多工作,容错率也提高了很多。
String toString() 含义 此方法用来将对象的信息以字符串方式反馈出去。 规定: 此方法是默认调用的(自动调用). 因此,子类的对象要想通过toString()方法来反馈信息,则必须重写它。 Demo <span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">package com.hqg.oop.d34; import com.hqg.oop.d33.a1.MyRectangle; //部分代码展示 public class MyCube { //属性 private MyRectangle mr; //将长方形作为当前长方体的底面 private int d; //长方体的高 //构造器 public MyCube() { } public MyCube( MyRectangle mr, int d ){ this.d = d; this.mr = mr; } //设定器 public void setMr( MyRectangle mr ){ this.mr = mr; } public void setD(int d) { this.d = d; } //访问器 public MyRectangle getMr() { return mr; } public int getD() { return d; } //求体积 public int volume(){ return mr.area() * d ; } //求表面积 public int areas(){ return 2 * mr.area() + 2 * mr.getH() * d + 2 * mr.getW() * d ; } //将所有信息以字符串反馈 public String toString(){ return "已知长方体的底面信息如下:\n" + mr + "\n同时,已知长方体的高度d = " + d + "\n则,这个长方体的体积volume = " + this.volume() + ",其表面积 ares = " + this.areas(); } } class LittleCube extends MyCube{ private String name ; public LittleCube() { super(); //自动的,是隐藏的. } public Little( String name) { super(); this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } //子类的对象要想通过toString()方法来反馈信息,则必须重写它。 public String toString(){ return MyCube.toString() + "它的名字是" + this.name() ; } } </span></span></span></span> boolean equals() 含义 此方法用来比较两个对象是否相等。 使用方式: boolean flag = obj1.equals( obj2 ); 即: 比较 obj1和obj2对应的对象是否相等。 规则: 祖先规定,当obj1和obj2引用的是同一个对象时,则结果为true; 反之,则结果为false。 结论: 在自定义类中,若要判断两个对象内容是否一样(相等),则必须重写 equals()方法。 目的: 为对象判断相等提供依据。 Demo <span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">packagecom.hqg.oop.d34.a1; public classStringTest { public static void main(String[] args) { int x = 10; int y = 20; boolean flag1 = x == y; String st1 = new String("中国北京") ; String st2 = new String("中国北京"); boolean flag = st1 == st2 ; //判断对象地址是否一样。 System.out.println("flag =" + flag ); flag = st1.equals( st2 ); //判断对象的内容是否完全一样。 System.out.println("flag =" + flag ); String s1 = "中国北京"; String s2 = "中国北京"; flag = s1 == s2 ; System.out.println( flag ? "s1和s2是同一个对象" : "s1和s2不是同一个对象"); } } </span></span></span></span> 由上面的demo判断equals()和==的区别及联系,下面看内存分析图,理解动态处理机制: 结论 1.凡是对象比较相等,使用 equals()方法; 1. 凡是基本类型的数据判断相等,则使用 == ; 2. ==用来判断对象地址是否一样; 3. Equals()判断对象内容是否完全一样。 业务思想 关于String toString()方法是对于项目中输出时的一个变更计划,无论是对于将对象的信息以字符串方式反馈出去,方便客户的理解和使用,还是在子类中输出方法的重写,都能够加强用户对于本应用程序的喜爱程度,此项必须加强使用。而对于boolean equals()的使用则是,给我们以警告,注意基本数据的比较与对象比较的区别和联系,引以为戒,以免造成不必要的损失。
背景 本篇博文主要讨论的是:局部变量和实例变量在内存的分配情况?是继[JAVA · 初级]:2.变量之后的又一次关于JAVA中变量的一些深入的解析。之前,关于变量的一些简单的总结已经’打包’至链接中的博文中,欢迎交流学习。 由于最近做项目的过程中,遇到的变量的变化情况,不能够很清楚的进行过程化处理,整理资料,总结一下。 Demo <span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">//声明并创建对象: 先声明了一个Employee类的对象变量emp01,用来存放对象的地址。 接着,创建一个Employee类的对象 //最后,将对象的地址赋给了emp01; //部分代码: package com.hqg.oop.d32.d2; //员工类: public class Employee { private int id; private String name; private boolean sex; private int age; private String department; public Employee() { } ==================================================== //应用类 package com.hqg.oop.d32.d2; public class Employee { public static void main(String[] args) { int x = 10; int y = 20; int z = x + y; System.out.println(x + " + " + y + " = " + z ); /*声明并创建对象: 先声明了一个Employee类的对象变量emp01,用来存放对象的地址。 接着,创建一个Employee类的对象 最后,将对象的地址赋给了emp01。 */ Employee emp01 = new Employee(); </span></span></span> 内存过程化分析图: 业务思想 主要是介绍关于内存中变量的过程化处理,作为编码人员,清楚的了解此过程,是在学习的基础上进行的业务细致化,建议平常多进行内存分析过程化处理。 古曰:“知己知彼,百战不殆”,在此基础上,我们能够更为清楚的了解变量的过程化,对我们十分重要!
定义 自增,自减运算符: ++ -- 它们是计算机专家自创的特有运算符。 ++ 有两种身份 a) 前加 ++a; 其规则是: a先增加,后使用。 b) 后加 a++; 其规则是: a先使用,后增加。 注意: 不论是前加 ++a; 还是后加 a++; 它们最终等价于 a = a + 1; --有两种身份: a) 前减 --a; 其规则是: a先减少,后使用。 b) 后减 a--; 其规则是: a先使用,后减少。 注意: 不论是前减 --a; 还是后减 a--; 它们最终等价于 a = a - 1; 深入理解 下面我们来围绕一个Demo来解释关于变量自身的自增和自减问题: <span style="font-size:18px;"><span style="font-family:SimSun;font-size:18px;"><span style="font-size:18px;">public class AddItself{ publicstatic void main(String[]args){ inti = 0; intj; for(j= 0;j < 5;j++){ i= i++; System.out.println("i= " + i); } } }</span></span></span> 在编译器上出了问题,java的编译器在遇到i++和i- -的时候会重新为变量运算分配一块内存空间,以存放原始的值,而在完成了赋值运算之后,将这块内存释放掉,下面首先看一下如果是j=i++的情况: 明白了上面的问题,让我们接下来看看i=i++的情况: 所以这样最后一次循环内的结果仍旧是i(即0): 学习小结 这种情况表明java的处理语法的机制不同,如果在程序中只输入i++就不会出现这个方面的问题,所以大家在以后的程序中如果使用到i=i++的时候要格外小心,一般只需要用i++就不会有问题了。 所以,关于的JAVA的学习必须不断地加强练习,尤其对于初学者来说,这段时间更是一个很好地提高期,借用极客快讯中一句劝谏的话:“不忘梦想和初心!”。