抽象方法与抽象类
当编写一个类时,常常会为该类定义一些方法,这些方法用以描述该类的行为,那些这些方法都有具体的方法体。但在某些情况下,某个父类只知道其子类应该包含哪些方法,但无法准确地知道这些子类如何实现这些方法。例如Shape类的area()方法,因为Shape类的不同子类对面积的计算方法不同,即Shape类无法准确地知道其子类计算面积的方法,因此area()方法只能留给子类实现。
在某些情况下会希望定义这样一种超类,在该超类中定义了一些子类应该包含的方法,但是在超类中不能给出这些方法的有意义的实现。例如,Animal类的eat()方法、Shape类的area()方法,无法给出有实际意义的实现,对于这类方法可以声明为抽象方法。
问题:
既然父类中不能给出抽象方法的实现,为什么还要在父类中添加这些方法呢?
抽象方法
抽象方法是使用abstract修饰的方法。将一个方法声明为抽象方法,从而要求子类必须重写该方法。
注意:
l 抽象方法没有方法实现,即没有方法体{},只有定义。
l 类中如果有抽象方法,该类必须是抽象类,必须使用abstract
l 对于抽象方法,abstract不能与private、static同时使用。为父类添加抽象方法,然后让子类实现,一个主要目的就是实现多态的效果,而实现多态效果需要两个前提:一是子类重写父类中的方法,二是使用父类引用调用子类重写后的方法,根据父类实际指向的对象调用相应的重写方法。如果将抽象方法声明为private,则子类中就无法重写该抽象方法;如果方法为static方法,则该方法属于类,而不属于某个对象,从而也就无法根据实际的指向的对象调用想用的方法。
抽象类
类定义中使用abstract修饰的类为抽象类。
注意:
l 从语法上讲,抽象类中可以没有抽象方法,但是没有实际意义
l 有抽象方法的类必须是抽象类
l 不能创建抽象类的对象,即不能new对象
l 抽象类可以当做一种引用类型来使用,声明变量
l 继承自抽象类的类,必需重写抽象类中的所有抽象方法,否则自身也使用abstract修饰,即也是抽象类。
抽象类的子类,会继承抽象类中的所有抽象方法,子类要么重写所有的抽象方法。如果有一个抽象方法的没有重写的话,子类中也有抽象方法。
说明:
抽象类只定义被其所有子类共享的一般形式,而让每个子类填充其细节。这种类确定了子类必需实现的方法。
注意:
有抽象方法的一定是抽象类。
错误,因为接口中的也有抽象方法,而且接口中的所有方法都是抽象方法。
接口的概念与定义
接口可以理解为抽象到不能再抽象的类,。可以认为类是一套体系,接口是另外一套体系,只不过类可以实现接口。但是不要将接口和类混为一谈
接口中的方法全部都是抽象方法,不能存在实现的方法。
接口使用interface关键字定义,接口的定义和类很相似。下面是经过简化的接口的一般形式:
access interface name {
return-type method-name1(parameter-list); //可以省略各种修饰符
return-type method-name2(parameter-list);
...
return-type method-nameN(parameter-list);
type varname1 = value; //可以省略各种修饰符
type varname2 = value;
..
type varnameN = value;
}
10.2 接口中的属性和方法
(1)接口中所有方法默认是公有的抽象方法。
隐式地标识为public、abstract,并且接口中的方法也只允许使用这两个修饰符。
注意,在抽象类中必需使用abstract关键字明确指定方法为抽象方法。
(2)在接口中所有变量默认为公有的静态常量。
被隐式地标识为public、static、final。这意味着实现接口的类不能修改它们。同时还必须初始化它们。
public interface A1 {
//接口中的属性必须public、static、final常量
public static final int I=10;
//接口中的属性public、static、final都可以省略
int J = 100;
//接口中的方法默认是public、abstract,所以public abstract可以省略
public abstract void print();
public void print2();
void print3();
}
注意:
l 接口能new对象吗?不可以
l 接口能作为一种类型定义引用变量吗? 可以 A1 a;
10.3 接口的实现
一旦定义了一个接口,一个或多个类就可以实现该接口。为了实现接口,在类定义中需要包含implements子句,然后创建接口定义的方法。
class classname implements interfacename {
//
}
注意:
(1)实现接口的类,必须实现接口的所有抽象方法,如果只实现了部分抽象方法,该类必须声明为抽象类。
(2)一个类可以实现多个接口,实现的多个接口用“,”隔开
(3)实现接口的类可以同时继承一个超类,必须是先继承后实现。
public interface A1 {
//接口中的属性必须是 public static final 常量
public static final int I=10;
//接口中的属性 public static final都可以省略
int J = 100;
//接口中的方法都是 public abstract 所以public abstract可以省略
public abstract void print();
public void print2();
void print3();
}
public interface A2 {
void show();
}
/**
* 类实现接口 使用implements
* 类可以实现多个接口,用 , 隔开
* 一个具体的类,实现接口,必须实现接口的所有抽象方法
* @author Administrator
*
*/
public class SubA implements A1,A2{
@Override
public void print() {
}
@Override
public void print2() {
}
@Override
public void print3() {
}
@Override
public void show() {
}
}
说明:
接口定义了一组抽象方法,实现该接口的类需要实现这些抽象方法,从而实现接口的类就具备了接口所规定的行为(功能)。
在Java中,接口可理解为对象间相互通信的协议,相当于模板。
接口继承
接口可以通过关键字extends继承另一个接口,其语法和类继承相同。如果类实现的接口继承自另外一个接口,则该类必需实现在接口继承链中定义的所有方法。
10.5 接口的实例
10.5.1 实例1
public interface Shape {
double area();
void show(); //打印输出类的成员信息
void draw(); //我是XXX(矩形、圆形、三角形)
}
定义Rectangle、Circle、Triangle类,添加适当的属性,并实现Shape接口
定义测试类测试Rectangle、Circle、Triangle类。在测试类中定义一个Shape[]数组,在该数组中存储不同的形状对象。通过循环,分别调用各个对象的area()、show()、draw()方法。
10.5.2 案例2
public interface IntStack {
void push(int item);
int pop();
}
10.7 抽象类和接口的区别
补充:
抽象类有构造方法,接口没有构造方法
类只能单继承,接口可以多重继承接口
抽象类中可以没有抽象方法,但是有抽象方法的类必须是抽象类。
包装类
一方面出于性能方面的考虑,java为数值使用基本类型,而不是对象。基本类型不是对象层次的组成部分,它们不继承Object。
另一方面有时需要创建表示基本类型的对象,例如集合类只处理对象。为了在类中存储基本类型,需要将基本类型包装到一个类中,为此Java为8种基本数据类型分别提供了对应的包装类。本质上这些类将基本类型包装到一个类中,因此通常将它们称为类型包装器。包装器类位于Java.lang包中。
八个包装类
Byte Short Integer Long Float Double Character Boolean
11.7.1 Character包装器
Character是char类型的包装器。Character的构造函数为:
Character(char ch)
其中,ch指定了将由即将创建的Character对象包装的字符。
为了获取Character对象中的char数值,可以调用charValue(),如下所示:
char charValue( )
该方法返回封装的字符。
11.7.2 Boolean包装器
Boolean是包装boolean值的包装器。它定义了以下构造函数:
Boolean(boolean boolValue)
Boolean(String boolString)
在第一个版本中,boolValue必须是true或false。在第二个版本中,如果boolString包含字符串“true”(大写或小写形式都可以),则新的Boolean对象将为真,否则,将为假。
为了从Boolean对象获取boolean值,可以使用booleanValue(),如下所示:
boolean booleanValue( )
该方法返回与调用对象等价的boolean型值。
11.7.3 数值类型的包装器类
1、构造器
所有数值类型包装器都定义了用于从给定数值或数值的字符串表示形式构造对象的构造函数,例如,下面是为Integer定义的构造器:
Integer(int num)
Integer(String str)//a123 “123”
如果str没有包含有效的数字值,则会抛出NumberFormatException异常。
2、从包装器对象中提取数值
最常用类型的包装器是那些表示数值的包装器。包括Byte、Short、Integer、Long、Float以及Double。所有这些数值类型包装器都继承自抽象类Number。Number声明了以不同数字格式从对象返回数值的方法,如下所示:
byte byteValue( )
double doubleValue( )
float floatValue( )
int intValue( )
long longValue( )
short shortValue( )
3、将包装器对象转换成字符串
类型包装器都重写了toString()方法,该方法可以将数值转换成字符串形式。
String str = Integer.toString(100);
自动装箱与自动拆箱
自动装箱是这样一个过程,只要需要基本类型的对象,就自动将基本类型自动封装(装箱)进与之等价的类型包装器中,而不需要明确地构造对象。自动拆箱是当需要时自动抽取(拆箱)已装箱对象数值的过程。不需要调用intValue()或doubleValue()这类方法。
自动装箱和自动拆箱特性极大地简化了一些算法的编码,移除了单调乏味的手动装箱和拆箱数值操作。它们还有助于防止错误。此外,它们对于泛型非常重要,因为泛型只能操作对象。最后,集合框架需要利用自动装箱特性进行工作。
案例:自动装箱与自动拆箱测试
11.7.2 数值与字符串形式之间的转换
最常见的编程杂务之一是将数值的字符串表示形式转换成数值。数值类型的包装器类为此提供了相应的方法。例如:
l Int类的parseInt()方法
l Long类的parseLong()方法
l Double类的parseDouble()方法
为了将数值转换成字符串形式,可以调用相应包装类的toString()方法。
示例:
说明:
各包装器类以静态方法的形式提供了许多很有用的辅助功能,请查阅帮助文档。
String.valueOf()
Int.toString();
Double.toString();
int I = 100;
String str = I + “”;
测试案例:
11.7.3 字符分类
Character类提供一些静态方法用于判断字符属于哪一类。
static boolean isDigit(char ch) | 如果ch是数字,则返回true。 |
static boolean isLetter(char ch) | 如果ch为字母,则返回true。 |
static boolean isLetterOrDigit(char eh) | 如果ch为字母或数字,则返回true。 |
static boolean isLowerCase(char ch) | 如果ch为小写字母,则返回true; |
static boolean isUpperCase(char ch) | 如果ch为大写字母,则返回true。 |
static boolean isSpaceChar(char ch) | 如果ch为空格字符,则返回true。 |
static boolean isWhitespace(char ch) | 如果ch为空白字符,则返回true。 |
11.7.4 包装器类中其他常用的常量和方法
Integer.MAX_VALUE//表示int数据类型的最大取值数:2 147 483 647
Integer.MIN_VALUE//表示int数据类型的最小取值数:-2 147 483 648
Integer.SIZE //长度,多少bit
Integer.valueOf(100); //根据整数创建Integer对象
Integer. valueOf("100"); //根据字符串创建Integer对象
常用类
String
11.2.1 String类介绍
(1)创建的每个字符串实际上都是String类的对象。即使是字符串字面值实际上也是String对象。
(2)String类型的对象是不可变的;一旦创建了一个String对象,其内容就不能再改变。即,一旦创建了一个String对象,就不能改变该字符串包含的字符。
所谓Stirng类型对象中的字符串是不可改变的,是指创建了String实例后不能修改String实例的内容。但是可以修改String引用变量,使其指向其他String对象。
当每次需要已存在字符串的修改版本时,会创建包含修改后内容的新String对象。原始字符串仍然没有改变。使用这种方法的原因是,实现固定的、不能修改的字符串与实现能够修改的字符串相比效率更高。
(3)对于那些需要能够修改的字符串的情况,Java提供了两个选择:StringBuffer和StringBuilder。这两个类都包含在创建之后可以进行修改的字符串。
(4)String、StringBuffer和StringBuilder类都是在java.lang包中定义的。这三个类都实现了CharSequence接口。
注意:API文档的使用
11.2.2 String类的构造方法
l String(); //创建不包含内容的字符串对象
l String(char[ ] chars)
l String(char[ ] chars, int startIndex, int numChars)
l String(String strObj)
l 还可以直接使用字符串字面创建String对象:String str = “abc”;
示例:StringMakeDemo
注意:没有使用单位字符作为参数的构造器。
//String(char c);
说明:
因为会为字符串字面值创建String对象,所以在能够使用String对象的任何地方都可以使用字符串字面值。
System.out.println("abc".length());
11.2.3 字符串比较
11.2.3.1 字符串相等性比较
String类重写了equals()方法,重写后的方法比较两个字符串对象的内容是否相同。
运算符“==”比较两个String引用是否指向同一个String对象。
示例:StringCompareDemo
11.2.3.2 其他比较方法
l boolean equalsIgnoreCase(String str)
将此String与另一个String比较,不考虑大小写。
将此 String 与另一个 String 比较,不考虑大小写。如果两个字符串的长度相同,并且其中的相应字符都相等(忽略大小写),则认为这两个字符串是相等的。
l boolean endsWith(String suffix)
测试此字符串是否以指定的后缀结束。
l boolean startsWith(String prefix)
测试此字符串是否以指定的前缀开始。
l int compareTo(String str)
按照字典顺序比较两个字符串。
其中,str是将要与调用String对象进行比较的String对象。返回的比较结果及其解释如下所示:
值 | 含 义 |
小于0 | 调用字符串小于str。 |
大于0 | 调用字符串大于str。 |
0 | 两个字符串相等。 |
l int compareToIgnoreCase(String str)
按字典顺序比较两个字符串,不考虑大小写。
11.2.4 字符串连接
String str1 = “abc” ;
String str2 = “def”;
String str3 = “hij”;
String str1 = str1 + str2;
String类型的引用,指向的字符串对象是不能修改的。
String表示不可变的字符串,只要创建了字符串对象,那么这个对象的内容就不能再改变。
API里面是否有拼接方法?
11.2.5 字符串查找
案例:StringSearchDemo
1.boolean contains(CharSequence s)
当且仅当此字符串包含指定的char值序列时,返回true。
CharSequence 表示字符串序列,是String的父类
2.int indexOf(int ch)和int indexOf(String str)
返回指定字符/子串第一次出现处的索引。
3.int lastIndexOf(int ch)和int lastIndexOf(String str)
返回指定字符/子串最后一次出现处的索引。
说明:
(1)当没有字符/子串没有出现时,返回值为-1。可以使用该方法判断字符/子串是否存在。
(2)可以使用下面这些重载形式指定查找的开始位置:
int indexOf(int ch, int startIndex)
int lastIndexOf(int ch, int startIndex)
int indexOf(String str, int startIndex)
int lastIndexOf(String str, int startIndex)
字符串修改
案例:StringModifyDemo
1.String concat(String str)
将指定字符串连接到此字符串的结尾,concat()与“+”执行相同的功能。
2.String replace(char oldChar, char newChar)
返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的。
3.String toLowerCase()
使用默认语言环境的规则将此String中的所有字符都转换为小写。
4.String toUpperCase()
使用默认语言环境的规则将此String中的所有字符都转换为大写。
5.String trim( )
返回字符串的副本,删除前导空白和尾部空白。
11.2.7 提取字符与子串
案例:GetCharsDemo
1.char charAt(int index)
返回指定索引处的char值。
2.char[ ] toCharArray()
将此字符串转换为一个新的字符数组。
3.String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。该子字符串始于指定索引处的字符,一直到此字符串末尾。
4.String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的beginIndex处开始,一直到索引endIndex-1处的字符。
11.2.8 其他字符串常用方法
案例:StringOtherMethDemo
1.int length()
返回此字符串的长度
注意:数组的长度为数组对象.length 属性
2.String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。最简单的使用方法是为参数regex
3.static String valueOf( int i)
将数据从内部格式转换成人类可读的形式。它是一个静态方法。
StringBuffer和StringBuilder
11.3.1 StringBuffer与StringBuilder类介绍
StringBuffer是String的对等类,提供了许多字符串功能。您可能知道,String表示长度固定、不可修改的字符序列。与之相对应,StringBuffer表示可增长、可写入的字符序列。StringBuffer允许在中间插入字符和子串,或在末尾追加字符和子串。StringBuffer能够自动增长,从而为这类添加操作准备空间,并且通常预先分配比实际所需更多的字符空间,以允许空间增长。
StringBuilder类是由JDK 5引入的,以增加Java的字符串处理能力,提供与StringBuffer相同的功能。
StringBuffer与StringBuilder的区别:
l StringBuffer类是线程安全的,而StringBuilder则不是,即不保证其对象的同步性,在多线程环境中是不安全的。
l StringBuilder在性能上要比StirngBuffer好一些。
扩容机制:16-16*2+2/更大
11.3.2 StringBuffer类的构造方法
StringBuffer( ) //默认预留16个字符的空间
StringBuffer(int size) //size指定预留的字符空间
StringBuffer(String str) //额外预留16个字符的空间 abc+16
StringBuffer(CharSequence chars) //额外预留16个字符的空间
提示:
再次分配内存空间是很耗时的操作。此外,频繁分配空间会产生内存碎片。
11.3.2 StringBuffer类的常用方法
1.append ()
append()方法将各种其他类型数据的字符串表示形式连接到调用StringBuffer对象的末尾。该方法有多个重载版本,下面是其中的几个:
StringBuffer append(String str)
StringBuffer append(int num)
StringBuffer append(Object obj)
2.insert ()
在指定位置插入参数提供的内容,返回修改后的该StringBuffer对象引用。该方法有多个重载版本,下面是其中的几个:
StringBuffer insert(int index, String str)
StringBuffer insert(int index, char ch)
StringBuffer insert(int index, Object obj)
3.StringBuffer delete (int start,int end) 10 包含5 不包含10
删除从start开始到end-1为止的一段字符序列,返回修改后的该StringBuffer对象引用。
4.StringBuffer deleteCharAt(int index) 8
移除指定位置的字符,返回修改后的该StringBuffer对象引用。
5.StringBuffer reverse()
将字符序列逆序,返回修改后的该StringBuffer对象引用。
6.StringBuffer setCharAt( (int index,char ch) 5 a
将指定索引处的字符设置为 ch,返回修改后的该StringBuffer对象引用。
public static void main(String[] args){
StringBuffer sb = new StringBuffer();
sb += “I”;
sb.append(“am”);
sb.append(true);
System.out.println(sb);
}
几点说明
(1)StringBuffer对象不能使用 += 赋值
(2)注意使用StringBuffer的append()方法连接字符串与使用“+”运算符直接连接String对象的区别。
案例:StringBufferDemo
11.3.3 长度与容量的概念
长度是指StringBuffer中实际保存的字符的个数,容量是指已经分配的空间大小。
1.int length()
获取StringBuffer对象的当前长度
2.void setLength(int len) 50 20 6 50 10
设置StringBuffer对象中字符串的长度。当增加字符串的大小时,会向末尾添加空字符。如果调用setLength()时使用的值小于length()返回的当前值,则超出新长度的字符将丢失。
3.int capacity( )
获取StringBuffer对象的当前容量
4.void ensureCapacity(int minCapacity)
设置缓存的大小。minCapacity指定了缓存的最小尺寸。(出于效率考虑,可能会分配比minCapacity更大的缓存。)
扩容后的数组长度为字节数组定长*2 +2 ,扩容不够时直接扩容到最大(指定大小)。
Math
package com.qf.math_class;
public class Test01 {
*/
public static void main(String[] args) {
System.out.println("求次方:" + Math.pow(3, 2));//9.0
System.out.println("求平方根:" + Math.sqrt(9));//3.0
System.out.println("获取绝对值:" + Math.abs(-100));//100
System.out.println("向上取整(天花板):" + Math.ceil(1.1));//2.0
System.out.println("向下取整(地板):" + Math.floor(1.9));//1.0
System.out.println("四舍五入:" + Math.round(1.5));//2
System.out.println("最大值:" + Math.max(10, 20));//20
System.out.println("最小值:" + Math.min(10, 20));//10
System.out.println("获取随机值(0包含~1排他):" + Math.random());//0.39661220991942137
//需求:随机出1~100的数字
System.out.println((int)(Math.random()*100) + 1);
}
}
2 Math相关面试题
package com.qf.math_class;
public class Test02 {
/**
* 知识点:Math类面试题
*/
public static void main(String[] args) {
//-2的31次方
System.out.println("获取int类型最小值:" + Integer.MIN_VALUE);//-2147483648
//2的31次方-1
System.out.println("获取int类型最大值:" + Integer.MAX_VALUE);//2147483647
//面试题:Math类的abs()是否会返回负数?
System.out.println(Math.abs(Integer.MIN_VALUE));//-2147483648
}
}
3 静态导入(了解)
当使用一个类里面的静态方法或者静态变量时,每次都需要写类名。如果不想写类名,想直接写方法名或者变量名,则可以考虑使用静态导入
语法:import static 包名.类名.*; //导入该类下的所有静态方法和常量8
例如:import static java.lang.Math.*; //导入Math下的所有方法和变量(包括常量)
则代码中可以直接使用方面和变量名,而不需要前缀Math。