Java高薪技术学习总结-03

Java高薪技术学习总结

Java高薪技术学习总结-03

3.3、了解和入门注解的应用

了解注解及Java提供的几个基本注解

  • 先通过@SuppressWarnings的应用让大家直观地了解注解:

    通过System.runFinallzersOnExit(true);的编译警告引出@SuppressWarnings("deprecation")
    
  • @Deprecated

    直接在刚才的类中增加一个方法,并加上@Deprecated标注,在另外一个类中调用这个方法。
    
  • @Override

    public boolean equals(Reflect other)方法与HashSet结合讲解
    
  • 总结

    注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事,标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
    
    看java.lang包,可看到jdk中提供的基本的annotation
    

Demo

public class AnnotationTest{
    //导致编译时不提示警告过时
    @SuppressWarnings("deprecation")
    public static void main(String[]args){
        //System.runFinallizersOnExit(true);//过时
    }
    //过时,别的类调用时就提示过时信息
    @Deprecated
    public static void sayHello(){
        System.out.println("hi,itcast");
    }
}

@override 指出函数是否是覆盖,不是覆盖编译器报错

Annotation Types Deprecated Override SuppressWarnings

3.4、注解的定义与反射调用

注解的应用结构图


注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类。就像你要调用某个类,得先有开发好这个类

在other里面去找注释类

自定义注解及其应用

*定义一个最简单的注解:public @interface MyAnnotation{}
*把它加在某个类上:@MyAnnotation public class AnnotationTest{}
*用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
*根据反射测试的问题,引出@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME分别对应:java源文件--->class文件--->内存中的字节码
    思考:@Override、@SuppressWarnings和@Deprecated这三个注解的属性值分别是什么?
*演示和讲解@Target元注解
    Target的默认值为任何元素,设置Target等于ElementType.METHOD。原来加载类上的注解就报错了,改为用数组方式设置{ElementType.METHOD,ElementType.Type}就可以了。
*元注解以及其枚举属性值不用记,只要会看jdk提供那几个基本注解的API帮助文档的定义或其源代码,查看就OK,或者直接查看Java.lang.annotation包下面的类、

Demo

import java.lang.reflect.Method;

import javax.jws.soap.InitParam;

@ItcastAnnotation
public class AnnotationTest {

    /**
     * @param args
     */
    @SuppressWarnings("deprecation")
    public static void main(String[] args) throws Exception{

        System.runFinalizersOnExit(true);
        if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
            ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);

    @Deprecated
    public static void sayHello(){
        System.out.println("hi,nihao");
    }
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


//建一个注释类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {

}

3.5、为注解增加各种属性

为注解增加基本属性

  • 什么是注解的属性

    一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是itcast的学生,否则,就不是。如果还想区分出是itcast哪个班的学生,这时候可以为胸牌再增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")
    
  • 定义基本类型的属性和应用属性

    在注解类中增加String color();
    @MyAnnotation(color="red");
    
  • 用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法。

    MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
    
    System.out.println(a.color());
    

可以认为上面这个@MyAnnotation是MyAnnotation类的一个实例对象

  • 为属性指定缺省值

    String color() default "yellow";
    
  • value属性

    String value() default "zxx";
    

如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。

为注解增加高级属性

  • 数组类型的属性

     int [] arrayAttr()default{1,2,3};
    
     @MyAnnotation(arrayAttr = {2,3,4})
    
     如果数组属性中只有一个元素,这时候属性值部分可以省略大括号
    
  • 枚举类型的属性

     EnumTest.TrafficLamp lamp();
    
     @MyAnnotation(lamp = EnumTest.TrafficLamp.GREEN)
    
  • 注解类型的属性

     MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
    
     @MyAnnotation(annotationAttr = @MetaAnnotation("yyy"))
    
     可以认为上面这个@MyAnnotation是MyAnnotation类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
    MetaAnnotation ma = myAnnotation.annotationAttr();
    System.out.println(ma.value());
    
  • 注解的详细语法可以通过看java语言规范了解,即看java的language specification

Demo

import java.lang.reflect.Method;
import javax.jws.soap.InitParam;

@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value="abc",arrayAttr=1)
public class AnnotationTest {
    /**
     * @param args
     */
    @SuppressWarnings("deprecation")
    @ItcastAnnotation("xyz")
    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        System.runFinalizersOnExit(true);
        if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
            ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
            System.out.println(annotation.color());
            System.out.println(annotation.value());
            System.out.println(annotation.arrayAttr().length);
            System.out.println(annotation.lamp().nextLamp().name());
            System.out.println(annotation.annotationAttr().value());
        }  
        Method mainMethod = AnnotationTest.class.getMethod("main", String[].class);
        ItcastAnnotation annotation2 = (ItcastAnnotation)mainMethod.getAnnotation(ItcastAnnotation.class);
        System.out.println(annotation2.value());
    }
    @Deprecated
    public static void sayHello(){
        System.out.println("hi,nihao");
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
    String color() default "blue";
    String value();
    int[] arrayAttr() default {3,4,4};
    EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
    MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
import java.util.Date;

public class EnumTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        WeekDay1 weekDay = WeekDay1.MON;
        System.out.println(weekDay.nextDay());

        WeekDay weekDay2 = WeekDay.FRI;
        System.out.println(weekDay2);
        System.out.println(weekDay2.name());
        System.out.println(weekDay2.ordinal());    
        System.out.println(WeekDay.valueOf("SUN").toString());
        System.out.println(WeekDay.values().length);

        new Date(300){};
    }

    public enum WeekDay{

        SUN(1),MON(),TUE,WED,THI,FRI,SAT;
        private WeekDay(){System.out.println("first");}
        private WeekDay(int day){System.out.println("second");}
    }    
    public enum TrafficLamp{
        RED(30){
            public  TrafficLamp nextLamp(){
                return GREEN;
            }
        },
        GREEN(45){
            public  TrafficLamp nextLamp(){
                return YELLOW;
            }            
        },
        YELLOW(5){
            public  TrafficLamp nextLamp(){
                return RED;
            }            
        };
        public abstract TrafficLamp nextLamp();
        private int time;
        private TrafficLamp(int time){this.time = time;}
    }
}

public @interface MetaAnnotation {
    String value();
}

输出结果:

red
abc
1
GREEN
flx
xyz

3.6、入门泛型的基本应用

体验泛型

  • jdk1.5以前的集合类中存在什么问题

     ArrayList collection = new ArrayList();
     collection.add(1);
     collection.add(1L);
     collection.add("abc");
     int i = (Integer)arrayList.get(1);//编译要强制类型转换且运行时出错!
    
  • JDK1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据。

    ArrayList<Integer> conllection2 = new ArrayList<Integer>();
    collection2.add(1);
    /*collection2.add(1L);
    collection2.add("abc");*///这两行代码编译时就报告了语法错误
    int i2 = collection2.get(0);//不需要再进行类型转换
    

泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中假如其它类型的数据,例如,用反射得到集合,再调用其add方法即可。

泛型是jdk1.5的所有新特性中最难深入掌握的部分,不过,我们在实际应用中不能掌握得那么深入,掌握泛型中一些最基本的内容就差不多了。没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

在jdk1.5中,你还可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告。

引入泛型以后,前面讲解反射的代码就可以改写成如下形式了,这种情况下创建实例对象时不需要类型转换:

Class<String> clazzString1 = String.class;

Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        ArrayList collection1 = new ArrayList();
        collection1.add(1);
        collection1.add(1L);
        collection1.add("abc");
        //int i = (Integer)collection1.get(1);

        ArrayList<String> collection2 = new ArrayList<String>();
        //collection2.add(1);
        //collection2.add(1L);
        collection2.add("abc");
        String element = collection2.get(0);    

        //new String(new StringBuffer("abc"));
        Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
        String str2 = constructor1.newInstance(/*"abc"*/new StringBuffer("abc"));
        System.out.println(str2.charAt(2));        
    }    
}

3.7、泛型的内部原理及更深应用

了解泛型


Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {

        //以下代码使用反射的机制去往ArrayList里存字符串,测试成功存入,即反射使用运行时期来处理程序的
        ArrayList<Integer> collection3 = new ArrayList<Integer>();
        System.out.println(collection3.getClass() == collection2.getClass());
        //collection3.add("abc");
        collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
        System.out.println(collection3.get(0));
    }    
}

3.8、泛型的通配符扩展应用

泛型中的?通配符


泛型中的?通配符的扩展


Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {

        ArrayList<Integer> collection3 = new ArrayList<Integer>();
        System.out.println(collection3.getClass() == collection2.getClass());
        //collection3.add("abc");
        collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
        System.out.println(collection3.get(0));

        printCollection(collection3);

        //Class<Number> x = String.class.asSubclass(Number.class);
        Class<?> y;
        Class<String> x ;//Class.forName("java.lang.String");


    public static void printCollection(Collection<?> collection){
        //collection.add(1);
        System.out.println(collection.size());
        for(Object obj : collection){
            System.out.println(obj);
        }
    }
    }

}

3.9、泛型集合的综合应用案例

泛型集合类的综合案例


Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {

        HashMap<String,Integer> maps = new HashMap<String, Integer>();
        maps.put("zxx", 28);
        maps.put("lhm", 35);
        maps.put("flx", 33);

        Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
        for(Map.Entry<String, Integer> entry : entrySet){
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
}

4.0、自定义泛型方法及其应用

由C++的模板函数引入自定义泛型


定义泛型方法






Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class GenericTest {

    add(3,5);
    Number x1 = add(3.5,3);
    Object x2 = add(3,"abc");

    swap(new String[]{"abc","xyz","itcast"},1,2);
    //swap(new int[]{1,3,5,4,5},3,4);

    private static <T> void swap(T[] a,int i,int j){
        T tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

    private static <T> T add(T x,T y){
        return null;
    }
}

4.1、自定义泛型方法的练习与类型推断总结

泛型方法的练习题


类型参数的类型推断


Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {

    add(3,5);
    Number x1 = add(3.5,3);
    Object x2 = add(3,"abc");

    swap(new String[]{"abc","xyz","itcast"},1,2);
    //swap(new int[]{1,3,5,4,5},3,4);

    Object obj = "abc";
    String x3 = autoConvert(obj);

    copy1(new Vector<String>(),new String[10]);
    copy2(new Date[10],new String[10]);        
    //copy1(new Vector<Date>(),new String[10]);

    private static <T> void fillArray(T[] a,T obj){
        for(int i=0;i<a.length;i++){
            a[i] = obj;
        }
    }
    private static <T> T autoConvert(Object obj){
        return (T)obj;
    }
    private static <T> void swap(T[] a,int i,int j){
        T tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

    private static <T> T add(T x,T y){
        return null;
    }

    public static void printCollection(Collection<?> collection){
        //collection.add(1);
        System.out.println(collection.size());
        for(Object obj : collection){
            System.out.println(obj);
        }
    }

    public static <T> void printCollection2(Collection<T> collection){
        //collection.add(1);
        System.out.println(collection.size());
        for(Object obj : collection){
            System.out.println(obj);
        }

    }


    public static <T> void copy1(Collection<T> dest,T[] src){

    }

    public static <T> void copy2(T[] dest,T[] src){

    }    
}

4.2、自定义泛型类的应用

定义泛型类型


Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {


        GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
        dao.add(new ReflectPoint(3,3));        
        //String s = dao.findById(1);
    }
}
import java.util.Set;

//dao data access object--->crud
public class GenericDao<E>  {
    public void add(E x){

    }

    public E findById(int id){
        return null;
    }

    public void delete(E obj){

    }

    public void delete(int id){

    }    

    public void update(E obj){

    }

    public static <E> void update2(E obj){

    }

    public E findByUserName(String name){
        return null;
    }
    public Set<E> findByConditions(String where){
        return null;
    }
}

4.3、通过反射获得泛型的实际类型参数

通过反射获得泛型的参数化类型

Demo

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

public class GenericTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        //从反射获得泛型的实际类型参数
        //Vector<Date> v1 = new Vector<Date>();
        Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
        Type[] types = applyMethod.getGenericParameterTypes();
        ParameterizedType pType = (ParameterizedType)types[0];
        System.out.println(pType.getRawType());
        System.out.println(pType.getActualTypeArguments()[0]);
    }
    public static void applyVector(Vector<Date> v1){//实际类型参数Data     
    }
}

4.4、类加载器及其委托机制的深入分析

类加载器的委托机制




类加载器之间的父子关系和管辖范围图


Demo

import java.util.Date;

public class ClassLoaderTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        System.out.println(
                ClassLoaderTest.class.getClassLoader().getClass().getName()
                );
        System.out.println(
                System.class.getClassLoader()
                );
        System.out.println("xxx");
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();
        while(loader != null){
            System.out.println(loader.getClass().getName());
            loader = loader.getParent();
        }
        System.out.println(loader);
    }
}

4.5、自定义类加载器的编写原理分析

编写自己的类加载器


4.6、编写对class文件进行加密的工具类

Demo

package cn.itcast.day2;
import java.util.Date;

public class ClassLoaderTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {

        //把加密好的ClassLoaderAttachment放回原来的路径上。访问不到了
        System.out.println(new ClassLoaderAttachment().toString());
    }
}
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
//用来加密的类加载器
public class MyClassLoader{
    public static void main(String[] args) throws Exception {
        String srcPath = args[0];
        String destDir = args[1];
        FileInputStream fis = new FileInputStream(srcPath);
        String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
        String destPath = destDir + "\\" + destFileName;
        FileOutputStream fos = new FileOutputStream(destPath);
        cypher(fis,fos);
        fis.close();
        fos.close();
    }

    private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
        int b = -1;
        while((b=ips.read())!=-1){
            ops.write(b ^ 0xff);
        }
    }

}
import java.util.Date;
//用来被加密的java文档
public class ClassLoaderAttachment extends Date {
    public String toString(){
        return "hello,itcast";
    } 
}

4.7、编写和测试自己编写的解密类加载器

Demo

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClassLoader extends ClassLoader{

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String srcPath = args[0];
        String destDir = args[1];
        FileInputStream fis = new FileInputStream(srcPath);
        String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
        String destPath = destDir + "\\" + destFileName;
        FileOutputStream fos = new FileOutputStream(destPath);
        cypher(fis,fos);
        fis.close();
        fos.close();
    }

    private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
        int b = -1;
        while((b=ips.read())!=-1){
            ops.write(b ^ 0xff);
        }
    }

    private String classDir;

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".class";
        try {
            FileInputStream fis = new FileInputStream(classFileName);
            ByteArrayOutputStream  bos = new ByteArrayOutputStream();
            cypher(fis,bos);
            fis.close();
            System.out.println("aaa");
            byte[] bytes = bos.toByteArray();
            return defineClass(bytes, 0, bytes.length);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public MyClassLoader(){

    }

    public MyClassLoader(String classDir){
        this.classDir = classDir;
    }
}
import java.util.Date;

public class ClassLoaderTest {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        System.out.println(
                ClassLoaderTest.class.getClassLoader().getClass().getName()
                );
        System.out.println(
                System.class.getClassLoader()
                );
        System.out.println("xxx");
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();
        while(loader != null){
            System.out.println(loader.getClass().getName());
            loader = loader.getParent();
        }
        System.out.println(loader);

        //System.out.println(new ClassLoaderAttachment().toString());
        System.out.println("xxx2");
        Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
        Date d1 =  (Date)clazz.newInstance();
        System.out.println(d1);
    }

}
import java.util.Date;

public class ClassLoaderAttachment extends Date {
    public String toString(){
        return "hello,itcast";
    } 
}

4.8、类加载器的一个高级问题的实验分析



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值