------- android培训、java培训、期待与您交流! ----------
1,String类
String类适合描述各种字符串事物,该类位于java.lang下,用于描述字符串对象。下面列出String类的常见操作:
1,获取
1.1 字符串包含的字符个数,也就是字符串的长度。
int length():获取字符串的长度。
1.2 根据位置获取位置上的某个字符。
char charAt(int index)
1.3 根据字符获取该字符在字符串中的位置。
int indexOf(int ch):返回的是ch在字符串中第一次出现的位置。
int indexOf(int ch,int fromIndex):从fromIndex指定位置开始,获取ch在字符串中出现的位置。
int indexOf(String str):返回的是str子串在字符串中第一次出现的位置。
int indexOf(String str,int fromIndex):从fromIndex指定位置开始,获取子串str在字符串中出现的位置。
int lastIndexOf(int ch):反向索引一个字符出现的位置
2,判断
2.1 字符串中是否包含某一个子串。
boolean contains(String str)
特殊之处:indexOf(String str):可以索引str第一次出现的位置,如果返回-1,表示该str不在字符串中。所以,也可以判断指定子串是否包含if(str.index("aa")!=-1) 而且该方法既可以判断,又可以获取出现的位置。
2.2 字符串中是否有内容。
boolean isEmpty():原理就是判断长度是否为0
2.3 字符串是否以指定内容开头。
boolean startsWith(Sting str)
2.4 字符串是否以指定内容结尾。
boolean endsWith(String str)
2.5 判断字符串内容是否相同,覆写了Object类中的equals方法
boolean equals(String str)
2.6 判断内容是否相同,并忽略大小写
boolean equalsIgnoreCase()
3,转换
3.1 将字符数组转成字符串。
构造函数:String(char[]ch)
String(char[] ch,int offset,int count):将字符数组的一部分转成字符串
静态方法:static String copyValueOf(char[] ch)
static String copyValueOf(char[]ch,int offset,int count)
static String valueOf(char[] ch)
3.2 将字符串转成字符数组。
char[] toCharArray()
3.3 将字节数组转成字符串。
构造函数:String(char[] ch)
String(char[] ch,int offset,intcount)
3.4 将字符串转成字节数组。
byte[] getBytes()
3.5 将基本数据类型转成字符串。
static String valueOf(int i);
static String valueOf(double d)等等
特殊之处:字符串和字节数组在转换过程中,是可以指定编码表的。
4,替换
String replace(char oldChar,char newChar)
String replace(string oldStr,String newStr)
注意:如果要替换的内容不存在,那么返回的还是原来的字符串
5,切割
String[] split(regex)
6,子串,获取字符串中的一部分
String substring(int beginIndex):从指定位置开始切割,一直到结尾
String substring(int beginIndex,int endIndex):从指定位置开始切割,到结束位置,包含头,不包含尾
注意:1,substring(int beginIndex,intendIndex)相当于substring(intbeginIndex,str.length())
2,如果传入的索引值大于字符串长度,将引发字符串角标越界异常
7,转换,去除空格,比较
7.1 将字符串转成大写或小写。
String toUpperCase()
String toLowerCase()
7.2 将字符串两端的多个空格去除。
String trim():只能去除字符串的两端空格,无法去除字符串内的空格
7.3 对两个字符串进行自然顺序的比较。
int compareTo(String str)
如果参数字符串等于此字符串,则返回值 0;如果此字符串按字典顺序小于字符串参数,则返回一个小于 0 的值;
如果此字符串按字典顺序大于字符串参数,则返回一个大于 0 的值。
8,经典字符串问题:
- class StringDemo
- {
- publicstatic void main(String[] args)
- {
- String s1 = "abc";
- String s2 = new String("abc");
- //以上的区别在于:第一种是一个对象,第二种是两个对象
- String s3 = "abc";
- System.out.println(s1==s2); //false
- System.out.println(s1==s3); //true,内存中已经存在abc,为了不浪费内存空间,s1和s3使用同一个字符串
- }
- }
class StringDemo
{
publicstatic void main(String[] args)
{
String s1 = "abc";
String s2 = new String("abc");
//以上的区别在于:第一种是一个对象,第二种是两个对象
String s3 = "abc";
System.out.println(s1==s2); //false
System.out.println(s1==s3); //true,内存中已经存在abc,为了不浪费内存空间,s1和s3使用同一个字符串
}
}
2,StringBuffer和StringBuilder
StringBuffer是字符串缓冲区,是一个容器,而且长度是可变的,可以操作多种数据类型,最终会通过toString()变成字符串。常见操作:
1,存储。
StringBuffer append():将指定数据作为参数添加到已有数据结尾处。
StringBuffer insert(int index,数据):可以将任意类型数据插入到指定index位置。
2,删除。
StringBuffer delete(int start,int end):删除缓冲区中的数据,包含start,不包含end
StringBuffer deleteCharAt(int index):删除指定位置的字符
3,获取
char charAt(int index):根据索引获取缓冲区中的字符
int indexOf(String str):根据字符串获取其在缓冲区的索引
int lastIndexOf(String str):根据字符串获取其中缓冲区的反向索引
int length():获取字符串缓冲区的长度
String substring(int start,int end):根据索引获取指定的子字符串,包含头,不包含尾
4,修改
StringBuffer replace(int start,intend,String str):根据索引将缓冲区的内容替换成str
void setCharAt(int index,char ch):根据索引将缓冲区中的某个字符换成指定字符
5,反转
StringBuffer reverse();:将字符串缓冲区进行反转
6,将缓冲区指定的数据存储到字符数组中
void getChars(int srcBegin,int srcEnd,char[]dst,int dstBegin):将字符串缓冲区从开始位置srcBegin,到结束位置srcEnd的内容,从字符数组dst的dstBegin位置开始全部拷贝
3,基本数据类型包装类
byte Byte
short Short
int Integer
long Long
boolean Boolean
float Float
double Double
char Character
1,基本数据类型包装类最常见的作用:,就是用于基本数据类型和字符串类型之间做转换
2,基本数据类型转成字符串
基本数据类型+""
基本数据类型.toString(基本数据类型值)
如:Integer.toString(34); //将34整数转成"34"
3,字符串转成基本数据类型
xxx a = Xxx.parseXxx(String);
inta = Integer.parseInt("34");
doubled = Double.parseDouble("12.34");
booleanb = Boolean.parseBoolean("true");
4,进制转换
toBinaryString():十进制转成二进制
toHexString():十进制转成十六进制
toOctalString():十进制转成八进制
parseInt(Stirng,radix) 其它进制转十进制
例如:
parseInt("110",2);
parseInt("3c",16);
5, 示例程序:
- class ParseDemo
- {
- publicstatic void sop(String str)
- {
- System.out.println(str);
- }
- publicstatic void main(String[] args)
- {
- //sop("intmax = "+Integer.MAX_VALUE);
- Integerx = 3; //自动装箱,new Integer(3)
- x= x + 2; //先调用x.intValue()方法,将x转成基本数据类型int,然后进行算术运算,将结果进行自动装箱,赋给x
- sop("x="+x); //输出x=5
- sop("================");
- Integer m = 128;
- Integer n = 128;
- Integer a = 127;
- Integer b = 127;
- sop("m==n:"+(m==n)); //false:因为要自动装箱,new Integer(),出现两个对象,地址值不同
- sop("a==b:"+(a==b)); //true:新特性,当数值在byte范围内,如果该数值已经存在,则不会开辟新的内存空间,所以a和b的地址相同
- }
- }
class ParseDemo
{
publicstatic void sop(String str)
{
System.out.println(str);
}
publicstatic void main(String[] args)
{
//sop("intmax = "+Integer.MAX_VALUE);
Integerx = 3; //自动装箱,new Integer(3)
x= x + 2; //先调用x.intValue()方法,将x转成基本数据类型int,然后进行算术运算,将结果进行自动装箱,赋给x
sop("x="+x); //输出x=5
sop("================");
Integer m = 128;
Integer n = 128;
Integer a = 127;
Integer b = 127;
sop("m==n:"+(m==n)); //false:因为要自动装箱,new Integer(),出现两个对象,地址值不同
sop("a==b:"+(a==b)); //true:新特性,当数值在byte范围内,如果该数值已经存在,则不会开辟新的内存空间,所以a和b的地址相同
}
}
4,Math类
|-- 静态的常量 圆周率public static final double PI
自然对数 public static final double E
|-- static int abs():绝对值
|-- static int max(int a,int b):返回较大的数
|-- static double ceil(double d):返回大于或者等于指定数最小数
|-- static double floor(double d):返回小于或者等于指定数最大数
|-- static double pow(int x,int y):幂运算 x的y次幂
|-- static int round(double d):四舍五入
|-- static double random():伪随机数 0.0-1.0
|-- 新的产生随机数的方式,用的是Random类 类中的方法nextInt(参数就是随机数的范围)
|-- 导包,导入的是包中的类java.util.Random;
5,Date类
|-- 构造方法 new Date()
|-- 构造方法 new Date(long date):毫秒值,可以将毫秒值,转成日期对象
|-- long getTime():有了日期对象,将当前时间转成毫秒值
|-- boolean after(Date d):当前日期在指定日期之后嘛
|-- boolean before(Date d):当前日期在指定日期之前嘛
|-- boolean equals(Date d):当前日期和指定时期相等吗
|-- int compareTo(Data d):比较两个日期的顺序
|--对日期进行格式化
|-- 建立SimpleDateFormat对象,在构造方法中,传递格式化的格式
|-- 使用SimpleDateFormat类中的方法,format方法,对日期对象进行格式化,返回一个字符串
|-- 打印字符串
示例代码:
- import java.util.*;
- import java.text.*;
- class DateDemo
- {
- publicstatic void main(String[] args)
- {
- Date d = new Date();
- System.out.println(d);
- //将模式封装到SimpleDateFormat对象中
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E hh:mm:ss");
- //调用format方法让模式格式化指定Date对象
- String time = sdf.format(d);
- System.out.println("time= "+time);
- }
- }
import java.util.*;
import java.text.*;
class DateDemo
{
publicstatic void main(String[] args)
{
Date d = new Date();
System.out.println(d);
//将模式封装到SimpleDateFormat对象中
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E hh:mm:ss");
//调用format方法让模式格式化指定Date对象
String time = sdf.format(d);
System.out.println("time= "+time);
}
}
6, Calendar类
|-- static Calendar getInstance():返回的是Calendar类对象
|-- int get(日历字段):返回给定日历字段的值。
|-- add(日历规则,偏移量)
|-- setTime(Date d):改变当前日历类,对应的日期
示例程序:
- import java.util.*;
- class CalendarDemo2
- {
- public static void main(String[] args)
- {
- Calendar c =Calendar.getInstance();
- c.add(Calendar.YEAR,4);
- c.add(Calendar.MONTH,3);
- printCalendar(c);
- }
- public static void printCalendar(Calendarc)
- {
- String[] mons = {"一月","二月","三月","四月",
- "五月","六月","七月","八月",
- "九月","十月","十一月","十二月"};
- String[] weeks ={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
- //获取年份中的某个月,传给index_mons当作索引进行查表
- int index_mons =c.get(Calendar.MONTH);
- //获取星期中的某一天,传给index_weeks当作索引进行查表
- int index_weeks =c.get(Calendar.DAY_OF_WEEK);
- sop(c.get(Calendar.YEAR)+"年");
- //sop((c.get(Calendar.MONTH)+1)+"月");
- sop(mons[index_mons]);
- sop(c.get(Calendar.DAY_OF_MONTH)+"日");
- //sop("星期"+(c.get(Calendar.DAY_OF_WEEK)-1));
- sop(weeks[index_weeks]);
- }
- public static void sop(Object obj)
- {
- System.out.println(obj);
- }
- }
import java.util.*;
class CalendarDemo2
{
public static void main(String[] args)
{
Calendar c =Calendar.getInstance();
c.add(Calendar.YEAR,4);
c.add(Calendar.MONTH,3);
printCalendar(c);
}
public static void printCalendar(Calendarc)
{
String[] mons = {"一月","二月","三月","四月",
"五月","六月","七月","八月",
"九月","十月","十一月","十二月"};
String[] weeks ={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
//获取年份中的某个月,传给index_mons当作索引进行查表
int index_mons =c.get(Calendar.MONTH);
//获取星期中的某一天,传给index_weeks当作索引进行查表
int index_weeks =c.get(Calendar.DAY_OF_WEEK);
sop(c.get(Calendar.YEAR)+"年");
//sop((c.get(Calendar.MONTH)+1)+"月");
sop(mons[index_mons]);
sop(c.get(Calendar.DAY_OF_MONTH)+"日");
//sop("星期"+(c.get(Calendar.DAY_OF_WEEK)-1));
sop(weeks[index_weeks]);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
打印结果:
注意:上面小程序,运用了常用的编程方法,查表法,值得深入学习。
由于Calendar类中的获取方法只能获取到当前时间年、月、日等等中具体的某一个时间,且返回值是int类型,这与我们日常习惯不符合,所以我们要通过查表法来返回我们日常习惯的用法。很简单,首先将需要查询的总体内容按照我们习惯一次存放到数组中,然后根据Calendar类中的get方法返回的int值当作索引,读取数组中的数据即可。
7, 正则表达式
正则表达式是一个强大的字符串处理工具,可以对字符串进行查找、获取、切割、替换等操作。正则表达式是用来操作字符串的,那么,我们来看看String类给我提供的几个特殊方法:boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
1,创建正则表达式
- /*
- 演示检查QQ号码输入是否合法
- 条件:只能是数字、开头不能是0、长度是5-11位
- */
- class CheckQQNumber
- {
- public static void main(String[] args)
- {
- //checkQQNum();
- checkQQWithRegex();
- }
- //使用正则表达式
- public static void checkQQWithRegex()
- {
- String qq = "124586521qq";
- String reg ="[1-9][0-9]{5,11}";
- boolean b = qq.matches(reg);
- System.out.println("该QQ号码是否合法? "+b);
- }
- //未使用正则表达式
- public static void checkQQNum()
- {
- String qq = "167ssss";
- if(qq.length() < 4 || qq.length() > 11)
- {
- System.out.println("QQ号码长度不合法");
- }
- else if(qq.substring(0,1).equals("0"))
- {
- System.out.println("不能是数字0开头");
- }
- else
- {
- char[] ch = qq.toCharArray();
- for(char c : ch)
- {
- if(c < 48 || c > 57){
- System.out.println("QQ中有特殊字符");
- return;
- }
- }
- System.out.println(qq+" 是合法的");
- }
- }
- }
/*
演示检查QQ号码输入是否合法
条件:只能是数字、开头不能是0、长度是5-11位
*/
class CheckQQNumber
{
public static void main(String[] args)
{
//checkQQNum();
checkQQWithRegex();
}
//使用正则表达式
public static void checkQQWithRegex()
{
String qq = "124586521qq";
String reg ="[1-9][0-9]{5,11}";
boolean b = qq.matches(reg);
System.out.println("该QQ号码是否合法? "+b);
}
//未使用正则表达式
public static void checkQQNum()
{
String qq = "167ssss";
if(qq.length() < 4 || qq.length() > 11)
{
System.out.println("QQ号码长度不合法");
}
else if(qq.substring(0,1).equals("0"))
{
System.out.println("不能是数字0开头");
}
else
{
char[] ch = qq.toCharArray();
for(char c : ch)
{
if(c < 48 || c > 57){
System.out.println("QQ中有特殊字符");
return;
}
}
System.out.println(qq+" 是合法的");
}
}
}
正则表达式就是一个字符串模板,可以匹配一批字符串,所以创建正则表达式就是创建一个特殊的字符串,通过jdk1.6 api文档的查阅,总结正则表达式常用的字符有如下:
除此之外,正则表达式中有一些特殊字符,这些特殊字符在正则表达式中有特殊的用途,如反斜线(\),如果需要匹配这些特殊字符,
必须首先将这些字符转义,也就是在前面添加一个反斜线(\)
上面的正则表达式依然只能匹配单个字符串,这是因为还未在正则表达式中使用预定义字符,正则表达式中的预定义字符可以匹配多个特殊字符。
有了上面的预定义字符后我们就可以创建更强大的正则表达式了,例如:
c\wt //可以匹配cat、cbt、cct、c0t...等一批字符串
\d\d\d-\d\d\d-\d\d\d\d //可以匹配形如000-000-0000形式的数字序列
在一些特殊情况,例如我们只想匹配a-f的字母,或者匹配除了ab之外的所有小写字母,或者想匹配中文字符,就需要使用方括号表达式了,
方括号表达式中所有的正则匹配模板必须要写在方括号内,即[]。
注意:如何匹配中文汉字字符?
方括号表达式几乎可以匹配任何字符,需要匹配中文汉字字符的话,可以利用[\\u0041-\\u0056]这样的形式,因为中文字符在UNICODE的编码
是连续的,只有找出所有中文字符中最小的和最大的UNICODE值,就可以利用上面提到的形式来匹配中文字符了。
图5,边界匹配符
》》Greedy(贪婪模式):除非另有表示,数量标识符默认采用贪婪模式。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。
》》Reluctant(勉强模式):用问号后缀(?)表示,它只会匹配最少的字符。也称为最小匹配模式。
》》Possessive(占有模式):用加号后缀(+),比较少用。
图6,三种匹配模式数量标识符
- class GreedyDemo {
- public static void main(String[] args) {
- String str = "hello , java!";
- System.out.println(str.replaceFirst("\\w*","@"));
- System.out.println(str.replaceFirst("\\w*?","@"));
- }
- }
class GreedyDemo {
public static void main(String[] args) {
String str = "hello , java!";
System.out.println(str.replaceFirst("\\w*","@"));
System.out.println(str.replaceFirst("\\w*?","@"));
}
}
输入结果:
2,使用正则表达式
Java中提供了Pattern类和Matcher类来操作正则表达式,这2个类都在java.util.regex包下,使用时必须要导包,在jdk1.6 API中的解释如下:
Pattern对象是正则表达式的编译表示形式,它指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher对象,
依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。因此,典型的调用顺序是
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
在仅使用一次正则表达式时,可以方便地通过此类定义 matches 方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句
boolean b = Pattern.matches("a*b", "aaaaab");
等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。 此类的实例是不可变的,可供多个并发线程安全使用。Matcher 类的实例用于此目的则不安全。
Matcher类有如下几个常用的方法
》》boolean find():返回目标字符串中是否包含与Pattern匹配的子串。
》》String group():返回上一次与Pattern匹配的子串。
》》int start():返回上一次与Pattern匹配的子串在目标字符串中开始的位置。
》》int end():返回上一次与Pattern匹配的子串在目标字符串中结束位置加1。
》》boolean lookingAt():返回目标字符串前面部分与Pattern是否匹配。
》》boolean matches():返回整个目标字符串与Pattern是否匹配。
》》Matcher reset():重置匹配器(可以将现有的Matcher对象应用于一个新的字符序列)。
- /*
- 演示Pattern和Matcher类的用法
- 在一句英文中,找出有3个字符组成的单词
- */
- import java.util.regex.*;
- public class PatternDemo {
- public static void main(String[] args) {
- // 目标字符串
- String str = "Welcome to the China Beijing haidian xi san qi";
- // 定义正则表达式,\b是取单词边界,否则将取出所有的由3个字符组成的单词
- String reg = "\\b[a-zA-Z]{3}\\b";
- // 调用Pattern类的compile方法,对正则表达式预先编译
- Pattern p = Pattern.compile(reg);
- // 调用Pattern类中的matcher方法,与字符串进行匹配
- Matcher m = p.matcher(str);
- // Matcher类的find方法返回与该模式匹配的下一个序列,返回boolean
- while (m.find()) {
- // Matcher类的group方法,返回所匹配的字符序列
- System.out.println(m.group());
- }
- }
- }
/*
演示Pattern和Matcher类的用法
在一句英文中,找出有3个字符组成的单词
*/
import java.util.regex.*;
public class PatternDemo {
public static void main(String[] args) {
// 目标字符串
String str = "Welcome to the China Beijing haidian xi san qi";
// 定义正则表达式,\b是取单词边界,否则将取出所有的由3个字符组成的单词
String reg = "\\b[a-zA-Z]{3}\\b";
// 调用Pattern类的compile方法,对正则表达式预先编译
Pattern p = Pattern.compile(reg);
// 调用Pattern类中的matcher方法,与字符串进行匹配
Matcher m = p.matcher(str);
// Matcher类的find方法返回与该模式匹配的下一个序列,返回boolean
while (m.find()) {
// Matcher类的group方法,返回所匹配的字符序列
System.out.println(m.group());
}
}
}
3,附:常用正则表达式匹配模式(网上摘录,正确性有待检测)
1,整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
2,只能输入数字:"^[0-9]*$"。
3,只能输入n位的数字:"^\d{n}$"。
4,只能输入至少n位的数字:"^\d{n,}$"。
5,只能输入m~n位的数字:。"^\d{m,n}$"
6,只能输入零和非零开头的数字:"^(0|[1-9][0-9]*)$"。
7,只能输入有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"。
8,只能输入有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"。
9,只能输入非零的正整数:"^\+?[1-9][0-9]*$"。
10,只能输入非零的负整数:"^\-[1-9][]0-9"*$。
11,只能输入长度为3的字符:"^.{3}$"。
12,只能输入由26个英文字母组成的字符串:"^[A-Za-z]+$"。
13,只能输入由26个大写英文字母组成的字符串:"^[A-Z]+$"。
14,只能输入由26个小写英文字母组成的字符串:"^[a-z]+$"。
15,只能输入由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"。
16,只能输入由数字、26个英文字母或者下划线组成的字符串:"^\w+$"。
17,验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,只能包含字符、数字和下划线。
18,验证是否含有^%&',;=?$\"等字符:"[^%&',;=?$\x22]+"。
19,只能输入汉字:"^[\u4e00-\u9fa5]{0,}$"
20,验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"。
21,验证InternetURL:"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"。
22,验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"
23,正确格式为:"XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX"。
24,验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"。
25,验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01"~"09"和"1"~"12"。
26,验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01"~"09"和"1"~"31"。
27,匹配中文字符的正则表达式: [\u4e00-\u9fa5]
28,匹配双字节字符(包括汉字在内):[^\x00-\xff]
29,匹配空行的正则表达式:\n[\s| ]*\r
30,匹配html标签的正则表达式:<(.*)>(.*)<\/(.*)>|<(.*)\/>
31,匹配首尾空格的正则表达式:(^\s*)|(\s*$)