Использование свойств (Руководство по программированию в C#)
Свойства сочетают в себе возможности полей и методов. Для пользователя объекта свойство, как представляется, является полем; для доступа к свойству требуется тот же синтаксис. При реализации класса свойство представляется в виде одного или двух блоков кода для методов доступа get и (или) set. Блок кода для get метода доступа выполняется при чтении свойства; блок кода для set метода доступа выполняется при назначении свойства значения. Свойство без метода доступа set доступно только для чтения. Свойство без метода доступа get доступно только для записи. Свойство, для которого определены оба этих метода, доступно для чтения и записи. Чтобы сделать свойство доступным только для чтения, можно использовать init метод set доступа вместо метода доступа.
В отличие от полей, свойства не классифицируются как переменные. Таким образом, нельзя передать свойство в качестве параметра ref или out .
Свойства имеют много использования: они могут проверять данные перед разрешением изменения; они могут прозрачно предоставлять данные в классе, где эти данные извлекаются из другого источника, например базы данных; они могут предпринять действия при изменении данных, таких как создание события или изменение значения других полей.
При объявлении свойств в блоке класса указывается уровень доступа поля, затем тип и имя свойства, а после этого блок кода, в котором объявляются методы доступа get и (или) set . Например:
public class Date < private int _month = 7; // Backing store public int Month < get =>_month; set < if ((value >0) && (value < 13)) < _month = value; >> > >
В этом примере Month объявляется как свойство, а метод доступа set обеспечивает установку значения Month в диапазоне от 1 до 12. Для отслеживания фактического значения свойство Month использует частное поле. Реальное расположение данных свойства часто называется «резервным хранилищем». Обычно свойства используют частные поля в качестве резервного хранилища. Поле помечается как частное для того, чтобы гарантировать возможность его изменения только посредством вызова свойства. Дополнительные сведения об ограничениях открытого и закрытого доступа см. в разделе Модификаторы доступа.
Автоматически реализуемые свойства поддерживают упрощенный синтаксис для простых объявлений свойств. Дополнительные сведения см. в разделе Автоматически реализуемые свойства.
Метод доступа get
Тело метода доступа get похоже на тело метода. Оно должно возвращать значение заданного типа свойства. Выполнение метода доступа get эквивалентно считыванию значения поля. Например, при возврате частной переменной из get метода доступа и оптимизации включен вызов метода доступа, вызов get метода доступа встраивается компилятором, поэтому затраты на вызов метода отсутствуют. Однако метод виртуального get доступа не может быть вложен, так как компилятор не знает во время компиляции, какой метод может вызываться во время выполнения. В следующем примере показан get метод доступа, возвращающий значение частного поля _name :
class Employee < private string _name; // the name field public string Name =>_name; // the Name property >
При ссылке на свойство (кроме случаев, когда свойство является целью присваивания) вызывается метод доступа get , который считывает значение свойства. Например:
var employee= new Employee(); //. System.Console.Write(employee.Name); // the get accessor is invoked here
Метод get доступа должен завершиться оператором return или throw , а управление не может вытекать из текста метода доступа.
Это неправильный стиль программирования для изменения состояния объекта с помощью get метода доступа.
Метод доступа get можно использовать для возврата значения поля напрямую или после вычисления. Например:
class Manager < private string _name; public string Name =>_name != null ? _name : "NA"; >
В предыдущем сегменте кода, если вы не назначаете свойству значение Name , оно вернет значение NA .
Метод доступа set
Метод доступа set похож на метод с типом возвращаемого значения void. В нем используется неявный параметр value , тип которого соответствует типу свойства. В следующем примере метод доступа set добавляется к свойству Name :
class Student < private string _name; // the name field public string Name // the Name property < get =>_name; set => _name = value; > >
При присвоении значения свойству вызывается метод доступа set с аргументом, содержащим новое значение. Например:
var student = new Student(); student.Name = "Joe"; // the set accessor is invoked here System.Console.Write(student.Name); // the get accessor is invoked here
Это ошибка использовать неявное имя value параметра для объявления локальной переменной в методе set доступа.
Метод доступа init
Код для создания метода доступа init аналогичен коду для создания метода доступа set , за исключением того, что используется ключевое слово init вместо set . Различие заключается в том, что метод доступа init можно использовать только в конструкторе или с помощью инициализатора объекта.
Замечания
Свойства могут быть помечены как public , private , protected , internal , protected internal или private protected . Эти модификаторы доступа определяют, каким образом пользователи класса смогут получать доступ к свойству. Методы доступа get и set для одного свойства могут иметь разные модификаторы доступа. Например, метод доступа get может иметь модификатор public , разрешающий доступ из-за пределов типа только для чтения, а метод доступа set — модификатор private или protected . Дополнительные сведения см. в статье Модификаторы доступа.
Свойство может быть объявлено как статическое с помощью ключевого слова static . Статические свойства доступны вызывающим в любое время, даже если экземпляр класса не существует. Дополнительные сведения см. в статье Статические классы и члены статических классов.
Свойство может быть помечено как виртуальное с помощью ключевого слова virtual. Виртуальные свойства позволяют производным классам переопределять поведение свойств с помощью переопределения ключевое слово. Дополнительные сведения об этих параметрах см. в разделе Наследование.
Свойство, переопределяющее виртуальное свойство, также можно запечатывать, указывая, что для производных классов он больше не является виртуальным. Наконец, свойство можно объявить абстрактным (abstract). Абстрактные свойства не определяют реализацию в классе, а производные классы должны писать собственную реализацию. Дополнительные сведения об этих параметрах см. в разделе Абстрактные и запечатанные классы и члены классов.
Использование модификаторов virtual, abstract или override в методе доступа статического (static) свойства является ошибкой.
Примеры
В этом примере демонстрируются свойства экземпляра, а также статические и доступные только для чтения свойства. Этот метод принимает введенное с клавиатуры имя сотрудника, увеличивает значение NumberOfEmployees на 1, после чего отображает имя и номер сотрудника.
public class Employee < public static int NumberOfEmployees; private static int _counter; private string _name; // A read-write instance property: public string Name < get =>_name; set => _name = value; > // A read-only static property: public static int Counter => _counter; // A Constructor: public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number: >
Пример скрытого свойства
В этом примере демонстрируется доступ к свойству базового класса, которое скрыто в производном классе другим свойством с таким же именем:
public class Employee < private string _name; public string Name < get =>_name; set => _name = value; > > public class Manager : Employee < private string _name; // Notice the use of the new modifier: public new string Name < get =>_name; set => _name = value + ", Manager"; > > class TestHiding < public static void Test() < Manager m1 = new Manager(); // Derived class property. m1.Name = "John"; // Base class property. ((Employee)m1).Name = "Mary"; System.Console.WriteLine("Name in the derived class is: ", m1.Name); System.Console.WriteLine("Name in the base class is: ", ((Employee)m1).Name); > > /* Output: Name in the derived class is: John, Manager Name in the base class is: Mary */
На что следует обратить внимание в предыдущем примере:
-
Свойство Name в производном классе скрывает свойство Name базового класса. В таком случае в объявлении свойства в производном классе используется модификатор new :
public new string Name
((Employee)m1).Name = "Mary";
Дополнительные сведения о скрытии элементов см. в разделе Модификатор new.
Пример переопределения свойства
В этом примере два класса ( Cube и Square ) реализуют абстрактный класс Shape и переопределяют его абстрактное свойство Area . Обратите внимание на использование модификатора override в свойствах. Программа принимает введенную длину стороны, на основании которой рассчитывает площади квадрата и куба. Также принимается введенное значение площади, на основании которой рассчитываются длины сторон квадрата и куба.
abstract class Shape < public abstract double Area < get; set; >> class Square : Shape < public double side; //constructor public Square(double s) =>side = s; public override double Area < get =>side * side; set => side = System.Math.Sqrt(value); > > class Cube : Shape < public double side; //constructor public Cube(double s) =>side = s; public override double Area < get =>6 * side * side; set => side = System.Math.Sqrt(value / 6); > > class TestShapes < static void Main() < // Input the side: System.Console.Write("Enter the side: "); double side = double.Parse(System.Console.ReadLine()); // Compute the areas: Square s = new Square(side); Cube c = new Cube(side); // Display the results: System.Console.WriteLine("Area of the square = ", s.Area); System.Console.WriteLine("Area of the cube = ", c.Area); System.Console.WriteLine(); // Input the area: System.Console.Write("Enter the area: "); double area = double.Parse(System.Console.ReadLine()); // Compute the sides: s.Area = area; c.Area = area; // Display the results: System.Console.WriteLine("Side of the square = ", s.side); System.Console.WriteLine("Side of the cube = ", c.side); > > /* Example Output: Enter the side: 4 Area of the square = 16.00 Area of the cube = 96.00 Enter the area: 24 Side of the square = 4.90 Side of the cube = 2.00 */
См. также
- Свойства
- Свойства интерфейса
- Автоматически реализуемые свойства
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Практическое руководство. Объявление и использование свойств чтения и записи (руководство по программированию на C#)
Свойства имеют все преимущества открытых членов данных, но не связаны с рисками незащищенного, неконтролируемого и несанкционированного доступа к данным объекта. Свойства объявляют методы доступа: специальные методы, которые назначают и извлекают значения из базового элемента данных. Метод доступа set присваивает значения членам данных, а метод доступа get извлекает их.
Это можно продемонстрировать на примере класса Person , который содержит два свойства: Name (string) и Age (int). Оба свойства предоставляют get методы доступа и set , поэтому они считаются свойствами чтения и записи.
Пример
class Person < private string _name = "N/A"; private int _age = 0; // Declare a Name property of type string: public string Name < get < return _name; >set < _name = value; >> // Declare an Age property of type int: public int Age < get < return _age; >set < _age = value; >> public override string ToString() < return "Name = " + Name + ", Age = " + Age; >> public class Wrapper < private string _name = "N/A"; public string Name < get < return _name; >private set < _name = value; >> > class TestPerson < static void Main() < // Create a new Person object: Person person = new Person(); // Print out the name and the age associated with the person: Console.WriteLine("Person details - ", person); // Set some values on the person object: person.Name = "Joe"; person.Age = 99; Console.WriteLine("Person details - ", person); // Increment the Age property: person.Age += 1; Console.WriteLine("Person details - ", person); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); > > /* Output: Person details - Name = N/A, Age = 0 Person details - Name = Joe, Age = 99 Person details - Name = Joe, Age = 100 */
Отказоустойчивость
В предыдущем примере свойства Name и Age являются открытыми и содержат одновременно методы доступа get и set . Открытые методы доступа позволяют любому объекту считывать и записывать эти свойства. Однако иногда желательно исключить один из методов доступа. Можно опустить set метод доступа, чтобы сделать свойство доступным только для чтения:
public string Name < get < return _name; >private set < _name = value; >>
Кроме того, можно сделать один метод доступа открытым, а другой оставить частным или защищенным. Дополнительные сведения см. в разделе Асимметричные методы доступа.
После объявления свойств их можно использовать в качестве полей класса . Свойства позволяют использовать естественный синтаксис при получении и установке значения свойства, как показано в следующих инструкциях:
person.Name = "Joe"; person.Age = 99;
В методе свойства set доступна специальная value переменная. Она содержит значение, заданное пользователем, например:
_name = value;
Обратите внимание на простой синтаксис приращения свойства Age для объекта Person :
person.Age += 1;
Если для моделирования свойств использовать отдельные методы set и get , аналогичный код может иметь следующий вид:
person.SetAge(person.GetAge() + 1);
В этом примере переопределяется метод ToString :
public override string ToString()
Обратите внимание, что ToString явно не используется в программе. Он вызывается по умолчанию вызовами WriteLine .
См. также
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Public string c что это
Кроме обычных методов в языке C# предусмотрены специальные методы доступа, которые называют свойства . Они обеспечивают простой доступ к полям классов и структур, узнать их значение или выполнить их установку.
Определение свойств
Стандартное описание свойства имеет следующий синтаксис:
[модификаторы] тип_свойства название_свойства < get < действия, выполняемые при получении значения свойства>set < действия, выполняемые при установке значения свойства>>
Вначале определения свойства могут идти различные модификаторы, в частности, модификаторы доступа. Затем указывается тип свойства, после которого идет название свойства. Полное определение свойства содержит два блока: get и set .
В блоке get выполняются действия по получению значения свойства. В этом блоке с помощью оператора return возвращаем некоторое значение.
В блоке set устанавливается значение свойства. В этом блоке с помощью параметра value мы можем получить значение, которое передано свойству.
Блоки get и set еще называются акссесорами или методами доступа (к значению свойства), а также геттером и сеттером.
Person person = new Person(); // Устанавливаем свойство - срабатывает блок Set // значение "Tom" и есть передаваемое в свойство value person.Name = "Tom"; // Получаем значение свойства и присваиваем его переменной - срабатывает блок Get string personName = person.Name; Console.WriteLine(personName); // Tom class Person < private string name = "Undefined"; public string Name < get < return name; // возвращаем значение свойства >set < name = value; // устанавливаем новое значение свойства >> >
Здесь в классе Person определено приватное поле name , которая хранит имя пользователя, и есть общедоступное свойство Name . Хотя они имеют практически одинаковое название за исключением регистра, но это не более чем стиль, названия у них могут быть произвольные и не обязательно должны совпадать.
Через это свойство мы можем управлять доступом к переменной name . В свойстве в блоке get возвращаем значение поля:
А в блоке set устанавливаем значение переменной name. Параметр value представляет передаваемое значение, которое передается переменной name.
В программе мы можем обращаться к этому свойству, как к обычному полю. Если мы ему присваиваем какое-нибудь значение, то срабатывает блок set , а передаваемое значение передается в параметр value :
person.Name = "Tom";
Если мы получаем значение свойства, то срабатывает блок get , который по сути возвращает значение переменной name:
string personName = person.Name;
То есть по сути свойство Name ничего не хранит, оно выступает в роли посредника между внешним кодом и переменной name.
Возможно, может возникнуть вопрос, зачем нужны свойства, если мы можем в данной ситуации обходиться обычными полями класса? Но свойства позволяют вложить дополнительную логику, которая может быть необходима при установке или получении значения. Например, нам надо установить проверку по возрасту:
Person person = new Person(); Console.WriteLine(person.Age); // 1 // изменяем значение свойства person.Age = 37; Console.WriteLine(person.Age); // 37 // пробуем передать недопустимое значение person.Age = -23; // Возраст должен быть в диапазоне от 1 до 120 Console.WriteLine(person.Age); // 37 - возраст не изменился class Person < int age = 1; public int Age < set < if (value < 1 || value >120) Console.WriteLine("Возраст должен быть в диапазоне от 1 до 120"); else age = value; > get < return age; >> >
В данном случае переменная age хранит возраст пользователя. Напрямую мы не можем обратиться к этой переменной — только через свойство Age. Причем в блоке set мы устанавливаем значение, если оно соответствует некоторому разумному диапазону. Поэтому при передаче свойству Age значения, которое не входит в этот диапазон, значение переменной не будет изменяться:
person.Age = -23;Консольный вывод программы:
1 37 Возраст должен быть в диапазоне от 1 до 120 37
Таким образом, свойство позволяет опосредовать и контролировать доступ к данным объекта.
Свойства только для чтения и записи
Блоки set и get не обязательно одновременно должны присутствовать в свойстве. Если свойство определяет только блок get , то такое свойство доступно только для чтения — мы можем получить его значение, но не установить.
И, наоборот, если свойство имеет только блок set , тогда это свойство доступно только для записи — можно только установить значение, но нельзя получить:
Person person = new Person(); // свойство для чтения - можно получить значение Console.WriteLine(person.Name); // Tom // но нельзя установить // person.Name = "Bob"; // ! Ошибка // свойство для записи - можно устновить значение person.Age = 37; // но нелзя получить // Console.WriteLine(person.Age); // ! Ошибка person.Print(); class Person < string name = "Tom"; int age = 1; // свойство только для записи public int Age < set < age = value; >> // свойство только для чтения public string Name < get < return name; >> public void Print()=> Console.WriteLine($"Name: Age: "); >
Здесь свойство Name доступно только для чтения, поскольку оно имеет только блок get :
public string Name < get < return name; >>
Мы можем получить его значение, но НЕ можем установить:
Console.WriteLine(person.Name); // получить можно person.Name = "Bob"; // ! Ошибка - установить нельзя
А свойство Age, наоборот, доступно только для записи, поскольку оно имеет только блок set :
public int Age < set < age = value; >>
Можно установить его значение, но нельзя получить:
person.Age = 37; // установить можно Console.WriteLine(person.Age); // ! Ошибка - получить значение нельзя
Вычисляемые свойства
Свойства необязательно связаны с определенной переменной. Они могут вычисляться на основе различных выражений
Person tom = new("Tom", "Smith"); Console.WriteLine(tom.Name); // Tom Smith class Person < string firstName; string lastName; public string Name < get < return $""; > > public Person(string firstName, string lastName) < this.firstName = firstName; this.lastName = lastName; >>
В данном случае класс Person имеет свойство Name, которое доступно только для чтения и которое возвращает общее значение на основе значений переменных firstName и lastName.
Модификаторы доступа
Мы можем применять модификаторы доступа не только ко всему свойству, но и к отдельным блокам get и set:
Person tom = new("Tom"); // Ошибка - set объявлен с модификатором private //tom.Name = "Bob"; Console.WriteLine(tom.Name); // Tom class Person < string name = ""; public string Name < get < return name; >private set < name = value; >> public Person(string name) => Name = name; >
Теперь закрытый блок set мы сможем использовать только в данном классе — в его методах, свойствах, конструкторе, но никак не в другом классе:
При использовании модификаторов в свойствах следует учитывать ряд ограничений:
- Модификатор для блока set или get можно установить, если свойство имеет оба блока (и set, и get)
- Только один блок set или get может иметь модификатор доступа, но не оба сразу
- Модификатор доступа блока set или get должен быть более ограничивающим, чем модификатор доступа свойства. Например, если свойство имеет модификатор public, то блок set/get может иметь только модификаторы protected internal, internal, protected, private protected и private
Автоматические свойства
Свойства управляют доступом к полям класса. Однако что, если у нас с десяток и более полей, то определять каждое поле и писать для него однотипное свойство было бы утомительно. Поэтому в .NET были добавлены автоматические свойства. Они имеют сокращенное объявление:
class Person < public string Name < get; set; >public int Age < get; set; >public Person(string name, int age) < Name = name; Age = age; >>
На самом деле тут также создаются поля для свойств, только их создает не программист в коде, а компилятор автоматически генерирует при компиляции.
В чем преимущество автосвойств, если по сути они просто обращаются к автоматически создаваемой переменной, почему бы напрямую не обратиться к переменной без автосвойств? Дело в том, что в любой момент времени при необходимости мы можем развернуть автосвойство в обычное свойство, добавить в него какую-то определенную логику.
Стоит учитывать, что нельзя создать автоматическое свойство только для записи, как в случае со стандартными свойствами.
Автосвойствам можно присвоить значения по умолчанию (инициализация автосвойств):
Person tom = new(); Console.WriteLine(tom.Name); // Tom Console.WriteLine(tom.Age); // 37 class Person < public string Name < get; set; >= "Tom"; public int Age < get; set; >= 37; >
И если мы не укажем для объекта Person значения свойств Name и Age, то будут действовать значения по умолчанию.
Автосвойства также могут иметь модификаторы доступа:
class Person < public string Name < private set; get;>public Person(string name) => Name = name; >
Мы можем убрать блок set и сделать автосвойство доступным только для чтения. В этом случае для хранения значения этого свойства для него неявно будет создаваться поле с модификатором readonly, поэтому следует учитывать, что подобные get-свойства можно установить либо из конструктора класса, как в примере выше, либо при инициализации свойства:
class Person < // через инициализацию свойства public string Name < get; >= "Tom"; // через конструктор public Person(string name) => Name = name; >
Блок init
Начиная с версии C# 9.0 сеттеры в свойствах могут определяться с помощью оператора init (от слова «инициализация» — это есть блок init призван инициализировать свойство). Для установки значений свойств с init можно использовать только инициализатор, либо конструктор, либо при объявлении указать для него значение. После инициализации значений подобных свойств их значения изменить нельзя — они доступны только для чтения. В этом плане init-свойства сближаются со свойствами для чтения. Разница состоит в том, что init-свойства мы также можем установить в инициализаторе (свойства для чтения установить в инициализаторе нельзя). Например:
Person person = new(); //person.Name = "Bob"; //! Ошибка - после инициализации изменить значение нельзя Console.WriteLine(person.Name); // Undefined public class Person < public string Name < get; init; >= "Undefined"; >
В данном случае класс Person для свойства Name вместо сеттера использует оператор init . В итоге на строке
Person person = new();
предполагается создание объекта с инициализацией всех его свойств. В данном случае свойство Name получит в качестве значения строку «Undefined». Однако поскольку инициализация свойства уже произошла, то на строке
person.Name = "Bob"; // Ошибка
мы получим ошибку.
Как можно установить подобное свойство? Выше продемонстрирован один из способов — установка значения при определении свойства. Второй способ — через конструктор:
Person person = new("Tom"); Console.WriteLine(person.Name); // Tom public class Person < public Person(string name) =>Name = name; public string Name < get; init; >>
Третий способ — через инициализатор:
Person person = new() < Name = "Bob">; Console.WriteLine(person.Name); // Bob public class Person < public string Name < get; init; >= ""; >
В принцпе есть еще четвертый способ — установка через другое свойство с модификатором init :
var person = new Person() < Name = "Sam" >; Console.WriteLine(person.Name); // Sam Console.WriteLine(person.Email); // Sam@gmail.com public class Person < string name = ""; public string Name < get < return name; >init < name = value; Email = $"@gmail.com"; > > public string Email < get; init; >= ""; >
В данном случае свойство Name управляет полем для чтения name . Благодаря этому перед установкой значения свойства мы можем произвести некоторую предобработку. Кроме того, в выражении init устанавливается другое init-свойство — Email, которое для установки значения использует значение свойства Name — из имени получаем значение для электронного адреса.
Причем если при объявлении свойства указано значение, то в конструкторе мы можем его изменить. Значение, установленное в конструкторе, можно изменить в инициализаторе. Однако дальше процесс инициализации заканчивается. И значение не может быть изменено.
Сокращенная запись свойств
Как и методы, мы можем сокращать определения свойств. Поскольку блоки get и set представляют специальные методы, то как и обычные методы, если они содержат одну инструкцию, то мы их можем сократить с помощью оператора => :
class Person < string name; public string Name < get =>name; set => name = value; > >
Также можно сокращать все свойство в целом:
class Person < string name; // эквивалентно public string Name < get < return name; >> public string Name => name; >
модификатор required
Модификатор required (добавлен в C# 11) указывает, что поле или свойства с этим модификатором обязательно должны быть инициализированы. Например, в следующем примере мы получим ошибку:
Person tom = new Person(); // ошибка - свойства Name и Age не инициализированы public class Person < public required string Name < get; set; >public required int Age < get; set; >>
Здесь свойства Name и Age отмечены как обязательные для инициализации с помощью модификатора required , поэтому необходимо использовать инициализатор для их инициализации:
Person tom = new Person < Name = "Tom", Age = 38 >; // ошибки нет
Причем не важно, устанавливаем эти свойства в конструкторе или инициализируем при определении, все равно надо использовать инициализатор для установки их значений. Например, в следующем примере мы получим ошибку:
Person bob = new Person("Bob"); // ошибка - свойства Name и Age все равно надо установить в инициализаторе public class Person < public Person(string name) < Name = name; >public required string Name < get; set; >public required int Age < get; set; >= 22; >
Класс String
Класс String очень часто используется программистами, поэтому его следует изучить очень хорошо.
Следует помнить, что объекты класса String являются неизменяемыми (immutable). Поэтому, когда вам кажется, что вы меняете строку, то на самом деле вы создаёте новую строку.
В Java есть специальные классы StringBuffer и StringBuilder, который допускают изменения в строке.
Классы String, StringBuffer, StringBuilder определены в пакете java.lang и доступны автоматически без объявления импорта. Все три класса реализуют интерфейс CharSequence.
Создать строку очень просто. Например, можно так:
String aboutCat = "Кот - это звучит гордо, а если наступить на хвост, то громко";
Можно создать массив строк:
String[] cats = ;
Можно создать пустой объект класса String:
String str = new String();
Можно создать строку через массив символов:
char[] chars = < 'c', 'a', 't' >; String str = new String(chars);
Есть ещё конструктор, позволяющий задать диапазон символьного массива. Вам нужно указать начало диапазона и количество символов для использования:
char[] chars = ; String str = new String(chars, 0, 3);
Можно создать объект класса String из объекта классов StringBuffer и StringBuilder при помощи следующих конструкторов:
String(StringBuffer объект_StrBuf) String(StringBuilder объект_StrBuild)
Операторы + и += для String
На языке Java знак плюс (+) означает конкатенацию строк (concatenation), иными словами — объединение строк.
String cat = "Кот"; String name = "Васька"; //складываем две строки и пробел между ними, чтобы слова не слиплись String fullname = cat + "" + name; // получится Кот Васька
Если один из операндов в выражении содержит строку, то другие операнды также должны быть строками. Поэтому Java сама может привести переменные к строковому представлению, даже если они не являются строками.
int digit = 4; String paws = " лапы"; String aboutcat = digit + paws; // хотя мы складываем число и строку, но все равно получим строку
За кулисами Java за нас преобразовало число 4 в строку «4».
Форматирование строк
Предположим у нас есть строковый ресурс:
У кота по имени Барсик четыре лапы, один хвост. Ему 5 лет
Чтобы вывести эту строку программно в элементе TextView, можно использовать код:
TextView tvCatsInfo = (TextView)findViewById(R.id.textView1); tvCatsInfo.setText(R.string.aboutcat);
Представьте себе, что у вас несколько котов. Можно, конечно, завести для каждого кота свою строку. Но ведь строки очень похожи, меняются только имена и возраст. Также можно менять число лап и хвостов (что вы курите?).
В таких случаях можно применить форматирование строк. Нужно определить слова, которые мы будем менять и заменить их на специальный набор символов, которые начинаются с символа процента, затем идет число, увеличивающееся на единицу, далее $s для строк или $d для чисел. Итак, изменим наш строковой ресурс так:
У кота по имени %1$s %2$s лапы, %3$s хвост. Ему %4$d лет
Внесём изменения в код:
String strBarsik = "Барсик"; String strPaws = "четыре"; String strTail = "один"; int year = 5; String strCats = getResources().getString(R.string.aboutcat); String strFinal = String.format(strCats, strBarsik, strPaws, strTail, year); infoTextView.setText(strFinal);
Если вас есть кот Васька и ему шесть лет, то добавляем две переменные и форматируем строку
String strVaska = "Васька"; year = 6; String strFinal = String.format(strCats, strVaska, strPaws, strTail, year); infoTextView.setText(strFinal);
Здесь показан простейший пример с форматированием. Помните о нём и применяйте в нужных местах.
Строковой ресурс
Строки желательно хранить в ресурсах (о ресурсах есть отдельная статья).
Программно доступ к строковому ресурсу делается так:
String catName = getResources().getString(R.string.barsik);
Извлечь строки из строковых массивов в ресурсах
Предположим, у вас есть строковый массив, определённый в файле strings.xml под именем cats_array. Тогда получить доступ к строкам из ресурсов можно так:
Resources res = getResources(); String[] cats = res.getStringArray(R.array.cats_array);
Методы
public char charAt (int index)
Возвращает символ с указанным смещением в этой строке. Отсчёт идёт от 0. Не надо использовать отрицательные и несуществующие значения, будьте серьёзнее. Для извлечения нескольких символов используйте getChars().
String testString = "Котёнок"; char myChar = testString.charAt(2); tv.setText(Character.toString(myChar)); // выводит третий символ - т
public int codePointAt(int index)
Возвращает Unicode-символ в заданном индексе
String testString = "Котёнок"; int myChar = testString.codePointAt(3); tv.setText(String.valueOf(myChar)); // возвращает 1105
public int codePointBefore(int index)
Возвращает Unicode-символ, который предшествует данному индексу
String testString = "Котёнок"; int myChar = testString.codePointBefore(4); tv.setText(String.valueOf(myChar)); // возвращает 1105
public int codePointCount(int start, int end)
Вычисляет количество Unicode-символов между позициями start и end
String testString = "Котёнок"; int myChar = testString.codePointCount(0, 3); tv.setText(String.valueOf(myChar)); // возвращает 3
public int compareTo(String string)
Сравнивает указанную строку, используя значения символов Unicode и вычисляет, какая из строк меньше, равна или больше следующей. Может использоваться при сортировке. Регистр учитывается. Если строки совпадают, то возвращается 0, если меньше нуля, то вызывающая строка меньше строки string, если больше нуля, то вызывающая строка больше строки string. Слова с большим регистром стоят выше слова с нижним регистром.
String testString = "Котёнок"; if (testString.compareTo("котёнок") == 0) < tvInfo.setText("Строки равны"); >else < tvInfo.setText("Строки не равны. Возвращено" + testString.compareTo("котёнок")); // возвращает -32 >
Отсортируем массив строк через пузырьковую сортировку.
String[] poem = < "Мы", "везём", "с", "собой", "кота" >; for (int j = 0; j < poem.length; j++) < for (int i = j + 1; i < poem.length; i++) < if (poem[i].compareTo(poem[j]) < 0) < String temp = poem[j]; poem[j] = poem[i]; poem[i] = temp; >> System.out.println(poem[j]); >
В результате мы получим:
Мы везём кота с собой
Как видите, от перемены мест слагаемых сумма сортировки коты не меняются.
public int compareToIgnoreCase (String string)
Сравнивает указанную строку, используя значения символов Unicode, без учёта регистра.
String testString = "Котёнок"; if (testString.compareToIgnoreCase("котёнок") == 0) < tv.setText("Строки равны"); // слова одинаковы, если не учитывать регистр >else
public String concat (String string)
Объединяет строку с указанной строкой. Возвращается новая строка, которая содержит объединение двух строк. Обратите внимание, что само имя метода содержит кота!
String testString = "Сук"; String newString = testString.concat("кот"); tv.setText(newString);
Метод выполняет ту же функцию, что и оператор + и можно было написать Сук + кот . Но настоящий кошатник будет использовать «кошачий» метод.
public boolean contains (CharSequence cs)
Определяет, содержит ли строка последовательность символов в CharSequence
String testString = "котёнок"; if(testString.contains("кот"))
public boolean contentEquals(CharSequence cs)
Сравнивает CharSequence с этой строкой.
public boolean contentEquals(StringBuffer strbuf)
Сравнивает StringBuffer с этой строкой
public static String copyValueOf(char[] data)
Создаёт новую строку, содержащую символы из указанного массива. Изменение массива после создания строки не изменяет созданную строку.
char[] textArray = ; String newString = String.copyValueOf(textArray); infoTextView.setText(newString);
public static String copyValueOf (char[] data, int start, int length)
Создаёт новую строку, содержащую указанные символы из массива data начиная с позиции start (нумерация начинается с нуля) длинной length.
Если указать индекс вне границ строки, то возникнет исключение StringIndexOutOfBoundsException.
char[] textArray = ; String newString = String.copyValueOf(textArray, 7, 4); infoTextView.setText(newString);
public boolean endsWith(String suffix)
Проверяет, заканчивается ли строка символами suffix.
String str1 = "Суккот"; if(str1.endsWith("кот")) infoTextView.setText("Слово заканчивается на котике"); else infoTextView.setText("Плохое слово. Нет смысла его использовать");
public boolean equals (Object string)
Сравнивает указанный объект и строку и возвращает true, если сравниваемые строки равны, т.е. содержит те же символы и в том же порядке с учётом регистра.
String str1 = "Кот"; String str2 = "Кошка"; if(str1.equals(str2)) infoTextView.setText("Строки совпадают"); else infoTextView.setText("Строки не совпадают");
Не путать метод с оператором ==, который сравнивает две ссылки на объекты и определяет, ссылаются ли они на один и тот же экземпляр. Смотри пункт Сравнение строк: equals() или ==?
public boolean equalsIgnoreCase(String string)
Сравнивает указанную строку с исходной строкой без учёта регистра и возвращает true, если они равны. Диапазон A-Z считается равным диапазону a-z.
String str1 = "Кот"; String str2 = "кот"; if(str1.equalsIgnoreCase(str2)) infoTextView.setText("Строки совпадают"); else infoTextView.setText("Строки не совпадают");
public static String format(Locale locale, String format, Object. args)
Возвращает отформатированную строку, используя прилагаемый формат и аргументы, локализованных в данной области. Например дату или время
// выводим число типа float с двумя знаками после запятой String.format("%.2f", floatValue);
Склеиваем два слова, которые выводятся с новой строки. При этом второе слово выводится в верхнем регистре.
String str1 = "Кот"; String str2 = "васька"; String strResult = String.format("%s\n%S", str1, str2); // выводим результат в TextView infoTextView.setText(strResult);
Конвертируем число в восьмеричную систему.
String str1 = "8"; int inInt = Integer.parseInt(str1); // конвертируем строку в число String strResult = String.format("(Восьмеричное значение): %o\n", inInt); infoTextView.setText(strResult);
По аналогии выводим в шестнадцатеричной системе
String str1 = "255"; int inInt = Integer.parseInt(str1); String strResult = String.format("(Шестнадцатеричное значение): %x\n", inInt); // число 255 будет выведено как ff infoTextView.setText(strResult);
Для верхнего регистра используйте %X, тогда вместо ff будет FF.
Для десятичной системы используйте %d.
Дату тоже можно выводить по разному.
Date now = new Date(); Locale locale = Locale.getDefault(); infoTextView.setText( String.format(locale, "%tD\n", now) + // (MM/DD/YY) String.format(locale, "%tF\n", now) + // (YYYY-MM-DD) String.format(locale, "%tr\n", now) + // Full 12-hour time String.format(locale, "%tz\n", now) + // Time zone GMT offset String.format(locale, "%tZ\n", now)); // Localized time zone abreviation
Допустим, при выводе double получается 3.0. Как вывести 3, т.е. без нуля. И с учётом того, что например 3.1 должно выводиться как 3.1. Округление здесь не поможет.
public static String fmt(double d) < if (d == (long) d) < return String.format("%d", (long) d); >else < return String.format("%s", d); >>
public byte[] getBytes()
Конвертируем строку в набор байтов.
String catName = "Барсик"; byte[] myByte = catName.getBytes(); Log.i(TAG, myByte.toString()); Log.i(TAG, new String(myByte)); // из массива байтов обратно в строку
public byte[] getBytes(String charsetName)
Возвращает отформатированную строку, используя прилагаемую кодировку.
String catName = "Барсик"; byte[] myByte = catName.getBytes(StandardCharsets.UTF_8); Log.i(TAG, myByte.toString()); Log.i(TAG, new String(myByte, StandardCharsets.UTF_8));
public void getBytes(int start, int end, byte[] data, int index) и другие перегруженные версии
Метод сохраняет символы в массив байтов, альтернатива методу getChars(). Часто используется при экспорте строк из различных источников, где используются другие символы Unicode. Например, Java по умолчанию использует 16-битовые символы Unicode, а в интернете часто строки используют 8-битовый код Unicode, ASCII и др.
public void getChars(int start, int end, char[] buffer, int index)
Метод для извлечения нескольких символов из строки. Вам надо указать индекс начала подстроки (start), индекс символа, следующего за концом извлекаемой подстроки (end). Массив, который принимает выделенные символы находится в параметре buffer. Индекс в массиве, начиная с которого будет записываться подстрока, передаётся в параметре index. Следите, чтобы массив был достаточного размера, чтобы в нём поместились все символы указанной подстроки.
String unusualCat = "Котёнок по имени Гав"; int start = 5; int end = 12; char[] buf = new char[end - start]; unusualCat.getChars(start, end, buf, 0); infoTextView.setText(new String(buf));
public int hashCode()
Возвращает целое число — хэш-код для данного объекта.
public int indexOf(int ch)
Ищет появление указанного символа и возвращает позицию индекса. Если символа нет, то возвращается -1.
String testString = "котёнок"; // символ ё встречается в четвёртой позиции (index = 3) infoTextView.setText(String.valueOf(testString.indexOf('ё')));
public int indexOf (int ch, int fromIndex)
Ищет индекс символа сh, начиная с позиции fromIndex
String testString = "котёнок"; // вернёт -1, так как после 5 символа буквы ё нет infoTextView.setText(String.valueOf(testString.indexOf('ё', 4)));
public int indexOf (String str)
Ищет подстроку str и возвращает индекс найденной подстроки. Если подстроки не существует, то возвращается -1.
String testString = "У окошка"; infoTextView.setText(String.valueOf(testString.indexOf("кошка")));
public int indexOf (String str, int fromIndex)
Ищет подстроку str, начиная с позиции fromIndex и возвращает индекс найденной подстроки. Если подстроки не существует, то возвращается -1.
String testString = "У окошка"; infoTextView.setText(String.valueOf(testString.indexOf("кошка", 2)));
Смотри также схожий метод lastIndexOf().
public String intern ()
public boolean isEmpty ()
Проверяет, является ли строка пустой
if(catname.isEmpty()) < // здесь ваш код >
Данный метод появился в API 9 (Android 2.1). Для старых устройств используйте String.length() == 0
public int lastIndexOf (String string) и другие перегруженные версии
Метод ищет в строке в обратном порядке и возвращает индекс последнего появления указанного символа. Если указанный символ не найден, то возвратится -1. Например, получить имя файла без расширения можно так:
filename.toString().substring(0, filename.getString().lastIndexOf("."));
public static String join (CharSequence delimiter, CharSequence. elements)
Метод появился в API 26. Склеивает слова указанным разделителем. Если слово является null, то null будет использоваться в качестве слова. Есть перегруженная версия.
String message = String.join("-", "Cat", "is", "cool"); // Cat-is-cool
В этом примере мы получаем позицию последней точки и получаем подстроку до неё.
public int length()
Возвращает длину строки
String testString = "котёнок"; infoTextView.setText(String.valueOf(testString.length())); // возвращает 7 (семь символов)
public boolean matches(String regularExpression)
Проверяет, соответствует ли строка регулярным выражениям.
public int offsetByCodePoints (int index, int codePointOffset)
Возвращает позицию, расположенную на расстоянии codePointOffset после начальной позиции, заданной параметром index
public boolean regionMatches (int thisStart, String string, int start, int length)
Метод сравнивает указанную часть строки с другой частью строки. Нужно задать индекс начала диапазон строки вызывающего объекта класса String. Строка для сравнивания передаётся в параметре string. Индекс символа, начиная с которого нужно выполнять сравнение передаётся в параметре start, а длина сравниваемой подстроки в параметре length.
public boolean regionMatches (boolean ignoreCase, int thisStart, String string, int start, int length)
Перегруженная версия. Метод сравнивает указанную часть строки с другой частью строки, игнорируя регистр.
public String replace(CharSequence target, CharSequence replacement) и другие перегруженные версии
Меняет символ или последовательность символов target на replacement
String source = "кит"; // меняем символы и на о infoTextView.setText(source.replace('и', 'о')); String source = "компот"; // меняем подстроку омпо на о infoTextView.setText(source.replace("омпо", "о")); // возвращается кот
public String replaceAll (String regularExpression, String replacement)
int i = Integer.parseInt("kitty123".replaceAll("[\\D]","")); int j = Integer.parseInt("123kitty".replaceAll("[\\D]","")); int k = Integer.parseInt("1k2it3ty".replaceAll("[\\D]","")); // Вернёт i = 123; j = 123; k = 123;
Смотри также пример в задачнике — Удаление гласных из строки.
public String replaceFirst (String regularExpression, String replacement)
Удаляет первые символы при помощи регулярного выражения.
Например, если нужно удалить нули в начале чисел 001, 007, 000024, то можно использовать такой вызов.
String s = "001234-cat"; String s = s.replaceFirst ("^0*", ""); // останется 1234-cat
public String[] split (String regularExpression) и другие перегруженные версии
Разбивает строку на массив из слов. Например, есть строка Васька Рыжик Мурзик Барсик и мы хотим получить массив имён котов:
String catNames = "Васька Рыжик Мурзик Барсик"; String aCats[] = catNames.split(" "); // по пробелу
aCats[0] = Васька
aCats[1] = Рыжик
aCats[2] = Мурзик
aCats[3] = Барсик
Также можно использовать регулярное выражение \\s+, чтобы учитывать любые типы пробелов, включая двойные и более пробелы подряд.
String[] words = someString.split("\\s+");
Будьте аккуратны при разбиении по запятой. Часто после запятой идёт пробел, поэтому нужно правильно установить разделитель.
String catNames = "Васька, Рыжик, Мурзик, Барсик"; String aCats[] = catNames.split("; "); // по запятой с пробелом
Если исходная строка начинается с разделителя, первым элементом результирующего массива будет пустая строка. Но если исходная строка заканчивается разделителем, то пустой строки не будет.
У перегруженной версии метода есть дополнительный параметр limit, определяющий, какое количество раз шаблон regex будет применяться к исходной строке.
Также следует быть аккуратным с разделителем |. В регулярных выражениях | является специальным символом. Чтобы использовать вертикальную черту в качестве разделителя, нужно экранировать этот символ с помощью двух обратных слешей «\\|».
public boolean startsWith(String prefix)
Проверяет, начинается ли строка символами prefix с начала строки
String str1 = "котлета"; if(str1.startsWith("кот")) infoTextView.setText("Слово содержит кота"); else infoTextView.setText("Плохое слово. Нет смысла его использовать");
public boolean startsWith(String prefix, int start)
Проверяет, начинается ли заданная строка символами prefix с указанной позиции.
String str1 = "Суккот"; if(str1.startsWith("кот", 3)) infoTextView.setText("Слово содержит кота"); else infoTextView.setText("Плохое слово. Нет смысла его использовать");
public CharSequence subSequence (int start, int end)
Аналогичен методу substring(), но может использоваться для CharSequence.
public String substring(int start) и другие перегруженные версии
Создаёт новую последовательность/строку с символами из данной строки начиная с позиции start до конца строки/заканчивая символом с позиции end. Новая строка содержит символы от start до end — 1, поэтому берём на один символ больше.
String testString = "скотина"; infoTextView.setText(testString.substring(1, 4)); // возвращается кот
Если указать индекс вне границ строки, то возникнет исключение StringIndexOutOfBoundsException.
public char[] toCharArray()
Копирует символы в этой строке в массив символов. Тот же результат можно получить через метод getChars(). Документация не рекомендует использовать данный метод, предлагая метод charAt().
String unusualCat = "Котёнок по имени Гав"; char[] yomoe = unusualCat.toCharArray(); infoTextView.setText(String.valueOf(yomoe[3]));
public String toLowerCase() и другие перегруженные версии
Преобразовывает строку в нижний регистр. Преобразованием управляет заданный по умолчанию региональный язык.
String cat = "Кот"; String lower = cat.toLowerCase(); infoTextView.setText(lower);
public String toString ()
Возвращает строку. Для самой строки, которая сама уже является строкой, возвращать строку бессмысленно (о, как я загнул). Но на самом деле этот метод очень полезен для других классов.
public String toUpperCase()
Преобразовывает строку в верхний регистр. Преобразованием управляет заданный по умолчанию региональный язык.
String cat = "Кот"; String upper = cat.toUpperCase(); infoTextView.setText(upper);
public String trim()
Удаляет пробелы в начале и в конце строки.
String str = " Hello Kitty ".trim(); infoTextView.setText(str);
public static String valueOf(long value) и другие перегруженные версии
Конвертирует содержимое (числа, объекты, символы, массивы символов) в строку.
int catAge = 7; // это число infoTextView.setText(String.valueOf(catAge)); // преобразовано в строку
Генерируем случайную строку
Допустим, нам нужна случайная строка из заданных символов.
private static final String mCHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; private static final int STR_LENGTH = 9; // длина генерируемой строки Random random = new Random(); public void onClick(View view) < TextView infoTextView = (TextView) findViewById(R.id.textViewInfo); infoTextView.setText(createRandomString()); >public String createRandomString() < StringBuilder builder = new StringBuilder(); for (int i = 0; i < STR_LENGTH; i++) < int number = random.nextInt(mCHAR.length()); char ch = mCHAR.charAt(number); builder.append(ch); >return builder.toString(); >
Сравнение строк: equals() или ==?
String str1 = "Мурзик"; String str2 = new String(str1); boolean isCat = str1 == str2; infoTextView.setText(str1 + " == " + str2 + " -> " + isCat);
Хотя в двух переменных содержится одно и то же слово, мы имеем дело с двумя разными объектами и оператор == вернёт false.
Однажды, когда деревья были большими, мне понадобилось сравнить две строки из разных источников. Хотя строки выглядели совершенно одинаково, сравнение при помощи оператора == возвращало false и путало мне все карты. И только потом я узнал, что нужно использовать метод equals(). Строка в Java — это отдельный объект, который может не совпадать с другим объектом, хотя на экране результат выводимой строки может выглядеть одинаково. Просто Java в случае с логическим оператором == (а также !=) сравнивает ссылки на объекты (при работе с примитивами такой проблемы нет):
String s1 = "hello"; String s2 = "hello"; String s3 = s1; String s4 = "h" + "e" + "l" + "l" + "o"; String s5 = new String("hello"); String s6 = new String(new char[]); infoTextView.setText(s1 + " == " + s2 + ": " + (s1 == s2)); // попробуйте и другие варианты // infoTextView.setText(s1 + " equals " + s2 + ": " + (s1.equals(s2))); // infoTextView.setText(s1 + " == " + s3 + ": " + (s1 == s3)); // infoTextView.setText(s1 + " equals " + s3 + ": " + (s1.equals(s3))); // infoTextView.setText(s1 + " == " + s4 + ": " + (s1 == s4)); // infoTextView.setText(s1 + " equals " + s4 + ": " + (s1.equals(s4))); // infoTextView.setText(s1 + " == " + s5 + ": " + (s1 == s5)); // false // infoTextView.setText(s1 + " equals " + s5 + ": " + (s1.equals(s5))); // infoTextView.setText(s1 + " == " + s6 + ": " + (s1 == s6)); // false // infoTextView.setText(s1 + " equals " + s6 + ": " + (s1.equals(s6)));
Сортировка символов в строке
Есть несколько способов сортировки символов в заданной строке. Рассмотрим их.
Способ первый. Конвертируем строку в массив символов через toCharArray(). Запускаем два цикла. При сравнении символов нужно учитывать их регистр, поэтому мы не только сравниваем разные символы, но и одинаковые, чтобы символы в верхнем и нижнем регистре тоже были отсортированы.
public void onClick(View view) < String myString = "Котёнок"; char[] charArray = myString.toCharArray(); for (int i = 0; i < charArray.length; i++) < for (int j = i + 1; j < charArray.length; j++) < if (Character.toLowerCase(charArray[j]) < Character.toLowerCase(charArray[i])) < swapChars(i, j, charArray); >> > System.out.println("Sorted string " + String.valueOf(charArray)); > private void swapChars(int i, int j, char[] charArray)
Способ прекрасно работает на английских символах. Но строка «Котёнок» даст неправильный результат: Ккноотё. Символ «ё» попал в самый конец массива.
Способ второй. Обходимся без циклов и используем метод sort().
String myString = "Котёнок"; char[] charArray = myString.toCharArray(); Arrays.sort(charArray); System.out.println("Sorted string " + String.valueOf(charArray));
Проблема с «ё» осталась. При этом появилась другая проблема — сначала идут символы в верхнем регистре, потом в нижнем. Получилось КОкнотё.
Переписываем пример с использованием Comparator.
String myString = "КотёнОк"; Character[] charArray = new Character[myString.length()]; for(int i = 0; i < myString.length(); i++)< charArray[i] = myString.charAt(i); >Arrays.sort(charArray, Comparator.comparingInt(Character::toLowerCase)); StringBuilder stringBuilder = new StringBuilder(charArray.length); for(Character character : charArray) < stringBuilder.append(character.charValue()); >System.out.println("Sorted string " + stringBuilder.toString());
Проблема с «ё» осталась, но с регистрами всё хорошо.
Кстати, в Java 8 можно использовать Stream для подобных операций. В любом случае будьте внимательные с символами другой локали. Ни один пример не решил наших проблем.
StringTokenizer
Класс StringTokenizer разбивает строку на токены с указанным разделителем.
StringTokenizer tokenizer = new StringTokenizer("Кот Васька", " "); while (tokenizer.hasMoreElements()) < System.out.println(tokenizer.countTokens() + " words remaining to print"); System.out.println(tokenizer.nextToken()); >/* 2 words remaining to print Кот 1 words remaining to print Васька */
Перевернуть строку
Рассмотрим различные способы решения задачи.
Первый способ — представить строку в виде массива символов. Затем проходимся по всей длине массива с конца и получаем новую строку.
String catName = "Барсик"; char[] array = catName.toCharArray(); String reversed = ""; for (int i = array.length - 1; i >= 0; i--) < reversed = reversed + array[i]; >mInfoTextView.setText(reversed);
Можно не создавать массив символов, а извлекать каждый символ через метод charAt() и снова использовать цикл, прибавляя к символу предыдущую строку.
String catName = "Барсик"; String reversed = ""; for (int i = 0; i < catName.length(); i++) < reversed = catName.charAt(i) + reversed; >mInfoTextView.setText(reversed);
Можно воспользоваться готовым методом reverse() класса StringBuffer/StringBuilder.
String catName = "Барсик"; String reversed; reversed = new StringBuilder(catName).reverse().toString(); mInfoTextView.setText(reversed);
Экзотический способ при помощи редко используемого класса Stack.
Разбиваем строку на массив символов и кладём каждый символ в стек. Далее начинаем брать элементы с верхушки стека. Из-за особенностей стека (первый вошёл, последний вышел), элементы будут браться задом-наперёд и мы получим нужный результат.
String catName = "Барсик"; String reversed = ""; Stack stack = new Stack<>(); for (Character character : catName.toCharArray()) < stack.add(character); >while (!stack.isEmpty()) < reversed = reversed + stack.pop(); >mInfoTextView.setText(reversed);
Особо упоротые программисты могут написать примеры с использованием рекурсии и побитового оператора XOR. Эти варианты не будем рассматривать.