目录
文章目录
一、包装类
二、处理对象
1.toString() 方法
2.== 和 equals()方法
三、类成员
1.理解类成员
2.单例类
3.final关键字
1.Final 成员变量
2.final局部变量
3.final修饰形参:
4.final 修饰基本类型和引用类型的区别
5.可执行"宏替换" 的final变量
6.final 方法
7.final 类
8.不可变类
9.缓存实例的不可变类
四、抽象类
五、接口
包装类实现基本类型变量和字符串之间的转换:
利用包装类的 parseXxx (String s) 静态方法 (除Character之外的所有包装类都提供了该方法)
利用包装类的提供的 valueOf(String s) 静态方法
把基本类型变量转换成字符串 , 更为简便的方法是: 基本类型+""
如: String str = 5 + "";
- 自动装箱:基本数据类型自动转换成包装类。
- 自动拆箱:包装类自动转换成基本数据类型。
数据缓存:
Java将一些创建成本大 , 需要频繁使用的对象缓存起来 , 从而提高程序的运行性能
toString()是Object类中的一个实例方法,所有的java对象都具有toString()方法,这个方法是对象的自我描述方法。
该方法实现的功能 : 打印该对象的时候 , 系统将会输出该对象的 " 自我描述 " 的信息 , 用以告诉外界该对象具有的状态信息
toString()方法是Object类里的一个实例方法
所有Java类都是Object类的子类
所有的Java对象都具有 toString()方法
所有的Java对象都可以和字符串进行连接运算
进行连接运算的时候,系统自动调用Java对象toString()方法的返回值和字符串进行连接运算
Object类提供的 toString() 方法总是返回该对象实现类的 "类名+@+hashCode"值
这个返回值不能真正实现"自我描述"的功能,需要重写Object类的 toString()方法
package process;
public class ToStringTest {
public static void main(String [] args)
{
Apple a = new Apple("红色", 19.0);
System.out.println(a);
}
}
class Apple
{
private String color;
private double weight;
//提供有参数的构造器
public Apple(String color ,double weight)
{
this.color=color;
this.weight=weight;
}
public void setColor(String name)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
public void setWeight(double weight)
{
this.weight = weight;
}
public double getWeight()
{
return this.weight;
}
//重写Object类的toString()方法
public String toString()
{
//return "一个苹果是,颜色是: " + color + ",重量是: " + weight;
return "Apple[color="+ color + " , weight=" + weight + "]";
}
}
== 和 equals() 是用于判断两个值是否相等.
判定规则:
如果两个变量是基本数值类型变量,则是判断两个变量只是否相等。
如果两个变量是引用类型变量,只有它们指向的是同一个对象的时候,返回值才是true。
package process;
public class EqualTest {
public static void main(String[] args)
{
int in = 65;
float i = 65.0f;
System.out.println("65和65.0是否相等?" + (in == i));
char ch = 'A';
System.out.println("65和'A'是否相等? " + (ch == in));
String str1 = new String ("hello");
String str2 = new String ("hello");
//由于str1和str2都是引用变量类型,
//他们通过两个关键字创建的String对象
//因此str1和str2两个对象不相等
//所以下面这行代码会输出false
System.out.println("str1和str2是否相等? " + (str1 == str2));
//由于String类重写了equals()
//String类的equals()比较的是两个字符串的值
//所以下面这行代码会输出 true
System.out.println("str1和str2是否相等? " + (str1.equals(str2)) );
//System.out.println("hello" == new EqualTEst());
//JVM会先使用常量池来管理"1"直接量
//再调用String类的构造器来创建一个新的String对象
//新创建的String对象被保存在堆内存中
//所以new String("1")创建了两个新字符串对象。
//因为对象不同 所以下面的代码最终会输出 false
String str3 = new String ("1");
System.out.println("str3和1是否相等? " + (str3 == "1" ));
}
}
String类重写了equals(),String类的equals()比较的是两个字符串的值。
常量池:
专门用于管理在编译时被确定并保存在已编译的 .class文件中的一些数据
它包括了关于类,方法,接口中的常量,还包括字符串常量
Object类的 equals()只是比较对象的地址
但是 equals()方法是可供重写的
即相等的条件完全由编写员决定
如果一个类始终只能创建一个实例,则这个类被称为单例类
package classmember;
public class SingletonTest {
public static void main(String [] args)
{
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a == b);
/*
通过封装的优势: 不允许自由访问类的成员变量和实现细节,
而是通过方法来控制合适暴露
从而保证Singleton类只能产生一个实例
所以 , 在main()方法中,看到两次产生的Singleton对象实际上是同一个对象
*/
}
}
class Singleton
{
//使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
//对该构造器使用private 修饰,隐藏构造器
private Singleton(){};
//提供一个静态方法 , 用于返回Singleton实例
//该方法可以加入自定义 ,保证只产生一个Singleton 对象
public static Singleton getInstance()
{
//如果instance为null ,表明还未创建过Singleton对象
//反之,则已经创建过一个Singleton对象
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
单例类:
在某些特殊的情况下,要求不允许自由创建该类的对象
这个时候就需要把该类的构造器使用 private 修饰,从而把该类的所有构造器隐藏起来
要隐藏该类的构造器,就需要通过一个公开的方法用于创建该类的对象,且该方法必须用static 修饰
该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象
也就无法保证只创建一个对象
final 关键字可以修饰类 , 变量 , 方法
它用于表示修饰的类 , 方法和变量不可变
- final 可修饰成员变量 (类变量 , 实例变量) , 也可以修饰局部变量 , 形参
- 被 final 修饰的变量不是不能赋值 , 而是 final 修饰的变量不可以被改变
- final 修饰的变量一旦获得了初始值 , 该变量就不能再重新被赋值
成员变量时随着类初始化或对象初始化而初始化的.
对于 final 修饰的成员变量而言 ,一旦有了初始值 , 其值就不能被重新赋值
如果成员变量指定初始值 , 也没有在初始化块 , 构造器中指定初始值 , 那么 这个 final 修饰的成员的变量值将全部为初始值 (比如 int 为 0 boolean 为false) 这个是没有意义的
所以规定: final 修饰的成员变量必须由程序员显式的指定初始值
package last;
public class FinalErrorTest {
//定义一个final 变量
//系统不会对final 变量进行默认初始化
final int age ;
{
//age 没有初始化 下面代码将发生错误
//System.out.println(age);
//通过方法访问成员变量 这是合法的 输出 0
PrintAge();
age = 6;
System.out.println(age);
}
public void PrintAge()
{
System.out.println(age);
}
public static void main(String [] args)
{
new FinalErrorTest();
}
}
final 成员在写显示初始化之前不能被直接访问 , 但可以通过方法来访问
但是 , 按照正常逻辑 , final成员变量在显式初始化之前是不允许被访问的
因此 , 要尽量避免在 final 成员变量显式初始化之前访问它
系统不会对局部变量初始化
局部变量必须由程序员显式初始化
因此使用final修饰局部变量时 , 既可以在定义时指定默认值 , 也可以不指定默认值
由系统根据传入的参数来完成初始化 , 使用 final 修饰的形参不能被赋值
final 修饰基本类型变量时,不能对基本类型变量重新赋值
对于引用类型变量而言,
final 只保证这个引用类型变量所引用的地址不会改变
即一直引用同一个对象
final 修饰的引用类型变量不能被重新赋值
但是可以改变引用类型变量所引用对象的内容
即final 修饰引用类型变量不能改变变量的地址,但是可以改变这个引用变量里面的值
package last;
import java.util.Arrays;
public class FinalReferenceTest {
public static void main(String [] args)
{
//final 修饰数组变量 . arr是一个引用变量
final int [] arr = {5,6,19,2};
System.out.println(Arrays.toString(arr));
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//对数组元素赋值 , 合法
arr[2]=-6;
//下面语句对arr 重新赋值 ,非法
//arr=null;
System.out.println(Arrays.toString(arr));
final Person a = new Person(6);
a.setAge(89);
System.out.println(a.getAge());
//下面语句重新对 p 重新赋值,不合法
//p=null;
}
}
class Person
{
private int age;
public Person(){};
public Person(int age)
{
this.age=age;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
}
final 相当于一个直接量的三个条件:
- 使用final修饰符修饰
- 在定义该final 变量时指定了初始值
- 该初始值可以在编译时就被确定下来
final 修饰符的一个重要用途就是"宏变量" ,
当定义了一个final 变量并且为其指定了初始值 ,
那么这个初始值在一开始就确定了下来
那么这个 final 变量本质上就是一个 " 宏变量 "
"宏变量"的用途: 编译器会把程序中所有用到该变量的地方直接替换成该变量的值
package last;
public class FinalReplaceTest {
public static void main(String [] args)
{
final int a = 5+2;
final double b = 1.2/3;
final String c = "疯狂" + "Java";
final String d = "疯狂Java讲义: " + 99.0 ;
final String f = "疯狂Java讲义: " + String.valueOf(99.0);
System.out.println(d=="疯狂Java讲义: 99.0");
System.out.println(f=="疯狂Java讲义: 99.0");
}
}
/*
Java会使用常量池来管理曾经用过的字符串直接量 , 例如执行 String a = "java"之后
常量池就会缓存一个字符串"java" ;
如果程序再次执行 String b ="java"
系统将会让b 直接指向常量池中的 "java"字符串
因此 a==b 返回true;
*/
final 宏变量:
Java会用常量池来管理曾经用过的字符串直接量
final 修饰的方法不可以被重写
不希望子类重写父类的某个方法 , 则可以提供 final 修饰该方法
对于一个private 方法,因为它仅仅在当前类中可见, 其子类无法访问该方法 , 所以子类肯定无法重写该方法
如果子类中定义一个与父类private方法 有相同方法名 相同形参列表 相同返回值列表的方法,这不是方法重写,而是重新定义了一个方法
'因此,即使使用 final 修饰符一个 private 访问权限的方法 , 依然可以在子类中定义于该方法有相同方法名 相同形参列表 相同返回值列表的方法,
//以下例子: 不是方法重写
public class Sub extends PrivateFinalMethodTest{
public void test()
{
}
}
class PrivateFinalMethodTest
{
private final void test()
{
}
}
final 修饰的方法仅仅是不能被重写 , 并不是不能被重载!!
package last;
public class FinalOverload {
public final void test()
{
}
public final void test(String str)
{
}
}
final 修饰的类不可以有子类
final修饰的类不可继承
不可变类的意思是创建该类的实例后 , 该实例的实例变量是不可改变的
可变类: 该类的实例变量的值是可变的 , 大部分创建的都是可变类
Java 提供的 8 个包装类和 java.lang.String 类都是不可变类
package last;
public class Person {
private final Name name;
//保证Person类的对象的name 的 firstName 不会改变
public Person (Name name)
{
this.name =name;
}
public Name getName()
{
return name;
}
public static void main(String [] args)
{
Name a = new Name("悟空","孙");
Person b = new Person(a);
System.out.println(b.getName().getFirstName());
a.setFirstName("八戒");
System.out.println(b.getName().getSecondName());
}
}
class Name {
private String firstName;
private String secondName;
public Name(){}
public Name(String firstName,String secondName)
{
this.firstName = firstName;
this.secondName = secondName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getFirstName()
{
return this.firstName;
}
public void setSecondName(String secondName)
{
this.secondName =secondName;
}
public String getSecondName()
{
return this.secondName;
}
}
创建自定义的不可变类:
- 使用 private 和 final 修饰符来修饰该类的成员变量
- 提供带参数的构造器 ( 或者返回该实例的类方法 ) 用于根据传入参数来初始化类里的成员变量
- 仅为该类的成员变量提供setter方法 , 因为普通方法无法修改 final 修饰的成员变量
- 如果有必要 , 重写 Object 类的 hashCode() 和 equals ()方法
不可变类的实例状态不可改变 , 可以很方便的被多个多想共享.
如果程序经常需要使用相同的不可变类实例 , 则应该考虑缓存这种不可变类的实例
package last;
//引用数组来作为一个缓存池
public class CacheImmutaleTest {
public static void main(String [] args)
{
CacheImmutale c1= CacheImmutale.valueOf("hello");
CacheImmutale c2 = CacheImmutale.valueOf("hello");
System.out.println(c1==c2);
}
}
class CacheImmutale
{
private static int MAX_SIZE = 10;
private static CacheImmutale [] cache = new CacheImmutale[MAX_SIZE];
private static int pos = 0;
private final String name;
private CacheImmutale(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public static CacheImmutale valueOf(String name)
{
for(int i=0;i
- 抽象方法和抽象类必须使用 abstract 修饰符定义
- 有抽象方法的类只能被定义成抽象类
- 抽象类里可以没有抽象方法
抽象方法和抽象类规则:
1. 抽象类必须用 abstract 修饰 , 抽象方法也必须用 abstract 修饰符来修饰
抽象方法不能有方法体 ( 既不能有花括号 )
2. 抽象类不能被实例化!!!!!
无法使用 new 关键字来调用抽象类的构造器创建抽象类的实例
即使抽象类里不包含抽象方法 , 这个抽象类也不能创建实例
3.抽象类可以包换成员变量 \ 方法(普通方法和抽象方法都可以) 构造器
初始化块 内部类(接口 枚举)
抽象类的构造器不能用于创建实例 , 主要是用于被其子类调用
4. 含有抽象方法的类只能被定义成抽象类
- 抽象方法:
- 直接定义了一个抽象方法 ;
- 继承了一个抽象父类 ,但没有完全实现父类包含的抽象方法
- 实现了一个接口 ,但没有完全实现接口包含的抽象方法
package Abstract;
public abstract class Shape {
public abstract double calPerimeter();
private String color;
public abstract String getType();
public Shape(){}
public Shape(String color)
{
System.out.println("执行Shape的构造器....");
this.color =color;
}
public void setColor(String color)
{
this.color =color;
}
public String getColor()
{
return this.color;
}
}
package Abstract;
public class Circle extends Shape{
private double radius;
public Circle(String color,double radius)
{
super(color);
this.radius =radius;
}
public void setRadius(double radius)
{
this.radius = radius;
}
public double calPerimeter()
{
return 2*Math.PI*radius;
}
public String getType()
{
return getColor() + " 圆";
}
public static void main(String [ ] args)
{
Shape a1=new Circle("黄色" ,3);
System.out.println(a1.getType());
System.out.println(a1.calPerimeter());
}
}
抽象类: " 有失有得" , "得" 指的是抽象类多了一个能力 : 抽象类可以包含抽象方法
"失" 指的是抽象类失去了一个能力 : 抽象类不能创建实例
抽象类不能用于创建实例 , 只能当作父类被其他子类继承
抽象类的作用:
从多个具有相同特征的类抽象出一个抽象类: 以这个抽象类作为子类的模板 , 从而避免了子类设计的随意性
抽象类体现的就是一种模板模式的设计 , 抽象类作为多个子类的通用模板 , 子类在抽象类的基础上进行扩展 , 改造 , 但是子类总体上会大致保留抽象类的行为方式
模板模式简单规则:
- 抽象父类可以只定义需要使用的某些方法 , 把不能实现的部分抽象成抽象方法 , 留给其子类去实现
- 父类种可能包含需要调用其他系列方法的方法 , 这些方法既可以由父类实现,也可以有子类实现. 父类里提供的方法只是一个通用算法,其实现也许并不完全由自身实现,而必须依赖于其子类的辅助
接口:多个类共同的公共的行为规范 , 这些行为是与外部交流的通道 , 这就意味着接口里通常是定义一组公用方法
定义接口使用 interface 关键字
接口定义:
1.修饰符可以时public 或者 省略,如果省略了public 访问控制符,则默认采用包权限访问控制符
即只有在相同包结构下才可以访问该接口
2.接口名应该与类名采取相同的命名规则
3.一个接口可以有多个父类接口,但是接口只能继承接口,不能继承类
package related;
public interface Output {
int MAX_SIZE_LINE = 50;
void out();
void getData(String str);
default void print(String...msg)
{
for(int i=0;i
接口定义:
接口定义的是一种规范:
接口里定义的是多个类共同的行为规范
因此接口里的常量 , 方法 ,内部类和内部枚举都是public 访问权限
Java9 为接口增加了一种新的私有方法 , 私有方法可以拥有方法体 ,但是不能用 defualt 修饰
私有方式可以是类方法 , 也可以是实例方法
接口里定义的静态常量: 他们是接口相关的 , 系统会自动为这些成员变量增加static 和 final 两个修饰符
接口里的成员变量总是用 public static final 修饰符来修饰
接口里没有构造器和初始化块 , 所以接口里定义的成员变量只能在定义时指定默认值
- 接口里定义的方法只能是抽象方法 , 类方法 , 默认方法或者私有方法
- 如果不是定义默认方法 , 类方法或者私有方法 ,系统将自动为普通方法增加 abstract 修饰符
- 接口里的普通方法总是使用 public abstract 来修饰
- 接口里的普通方法不能有方法体 (即没有花括号)
- 类方法 , 默认方法 ,私有方法都必须由方法体(即有花括号)
接口里定义的内部类 , 内部接口 , 内部枚举默认都采用 public static 两个修饰符
不管定义时是否指定这两个修饰符 , 系统都会自动使用 public static 对它们进行修饰
类方法(用static 修饰)
默认方法(default修饰)
私有方法(private 修饰)
静态方法(private static 修饰)
接口里定义的成员变量只能是常量
默认方法必须使用 default 修饰 , 该方法不能使用 static 修饰
无论程序是否指定 , 默认方法总是使用 public 修饰
接口的默认方法就是实例方法
接口里的成员变量默认是使用 public static final 修饰的
因此及时另一个类处于不同的包下 , 也可以通过接口来访问接口里的成员变量
接口可以被当成一种特殊的类. 因此一个Java 源文件里面最多只能有一个 public 接口 , 如果一个 Java 源文件里定义了一个 public 接口 ,则该源文件的主文件名必须与该接口名相同
接口的继承和类的继承不一样 , 接口完全支持多继承 , 即一个接口可以有多个直接父接口
和类继承相似 , 子接口扩展某个父接口时 , 将会获得父接口里定义的所有抽象方法 常量
一个接口继承多个父接口时 , 多个父接口陪在 extends 关键字之后 , 多个父接口之间以英文逗号隔开
package related;
interface interfaceA {
int a =5;
void testA();
}
package related;
interface interfaceB {
int b = 6;
void testB();
}
package related;
interface interfaceC extends interfaceA , interfaceB
{
int c =7;
void testC();
}
package related;
public class InterfaceExtendsTest {
public static void main(String [] args)
{
System.out.println(interfaceC.a);
System.out.println(interfaceC.b);
System.out.println(interfaceC.c);
}
}
接口不能用于创建实例 , 但接口可以用于声明引用类型变量
接口的主要用途就是被实现类是实现
接口的用途:
一个类实现了一个或者多个接口之后 , 这个类必须完全实现这些接口里所定义的全部抽象方法 (也就是重写这些抽写方法)
否则 , 该类将保留从父接口中那里继承到的抽象方法 , 该类也必须定义为抽象类
一个类可以实现一个或者多个接口
继承使用extends 关键字,实现则使用 implements 关键字
让类实现接口需要在类定义后增加 implements
实现接口方法时 , 必须使用 public 访问修饰符 , 因为接口里的方法都是 public 的 ,而子类的重写父类方法访问权限时只能是更大或者相等 , 所以实现类的方法只能用 public 修饰
接口不能显式继承任何类 , 但是所有接口类型的引用变量都可以直接赋给 Object 类型的引用变量
接口和抽象类都不能被实例化 , 它们都位于继承树的顶端 , 用于被其他类实现和继承
接口和抽象类都可以包含抽象方法 , 实现接口或继承抽象类的普通子类都必须实现这些抽象方法
- 接口与接口之间是继承关系
- 一个接口可以继承多个接口
- 一个类可以实现多个接口